2014-10-28 10 views
1

Довольно ясно, что использование Task с async/await вместо Thread - это способ сделать асинхронные вызовы. Мой вопрос в том, что есть способ контролировать потоки, которые порождаются при выполнении этих задач? Это значит, что я могу выбрать оптимальное количество Заданий для планирования, чтобы потоки не потребляли много циклов процессора одновременно (при условии, что Задачи имеют интенсивность процессора).Как узнать количество созданных потоков и ограничить задачи

Приведем пример ниже (также упоминается вывод). Хотя программа завершается за 5 секунд, она создала два потока (Id = 1,4) для выполнения всех задач. Если я увеличиваю количество заданий до 6 вместо 2, он создает 4 темы. Я знаю, что эти потоки CLR сопоставляются с потоками ОС (которые составляют всего 4 на моей машине), но я хотел бы знать, как они отображаются (вместе с задачами) и соответствующим использованием ЦП. Есть ли способ достичь этого?

TEST CODE

static void Main(string[] args) 
    { 
     RunTasksWithDelays().Wait(); 
    } 

    static async Task RunTasksWithDelays() 
    { 
     Stopwatch s = Stopwatch.StartNew(); 

     Console.WriteLine("ThreadId=" + Thread.CurrentThread.ManagedThreadId); 
     Task task1 = LongRunningTask1(); 
     Task task2 = LongRunningTask2(); 

     await Task.WhenAll(task1, task2); 
     Console.WriteLine("total seconds elapsed: " + s.ElapsedMilliseconds/1000); 
    } 

    static async Task LongRunningTask1() 
    { 
     Console.WriteLine("1 start " + DateTime.Now); 
     Console.WriteLine("ThreadId=" + Thread.CurrentThread.ManagedThreadId); 
     await Task.Delay(5000); 
     Console.WriteLine("ThreadId=" + Thread.CurrentThread.ManagedThreadId); 
     Console.WriteLine("1 end " + DateTime.Now); 
    } 

    static async Task LongRunningTask2() 
    { 
     Console.WriteLine("2 start " + DateTime.Now); 
     Console.WriteLine("ThreadId=" + Thread.CurrentThread.ManagedThreadId); 
     await Task.Delay(2000); 
     Console.WriteLine("ThreadId=" + Thread.CurrentThread.ManagedThreadId); 
     Console.WriteLine("2 end " + DateTime.Now); 
    } 

ВЫВОД

ThreadId=1 
1 start 28-10-2014 18:27:03 
ThreadId=1 
2 start 28-10-2014 18:27:03 
ThreadId=1 
ThreadId=4 
2 end 28-10-2014 18:27:05 
ThreadId=4 
1 end 28-10-2014 18:27:08 
total seconds elapsed: 5 
Press any key to continue . . . 
+1

Не уверен, если я понимаю ваш вопрос, но, насколько я знаю, задачи Parallel Библиотека интеллектуально идентифицирует эффективное количество потоков для обработки задач на основе конфигурации вашего оборудования. Одним из пунктов TPL является то, что вы не беспокоитесь о планировании и объединении потоков. – Iravanchi

ответ

3

с помощью задачи с async/await, а Thread - это способ сделать асинхронные вызовы ... (при условии, что Tasks интенсивно работают в ЦП).

Асинхронные (обычно связанные с I/O) задачи не требуют интенсивного использования ЦП. Поэтому вам не о чем беспокоиться.

Если вы являются делать ресурсоемкие работы, смотрите в Parallel/Parallel LINQ или TPL DataFlow, оба из которых имеют встроенные опции для дросселирования. В частности, поток данных TPL хорош для микширования кода ввода-вывода и процессора.

+0

Я использую Parallel.ForEach для выполнения вычислений в каждой из задач, но у меня слишком много таких задач. Существует некоторый ввод-вывод, а также мне нужно получить записи БД, используемые в вычислениях. Но не уверен, как контролировать выполнение потоков в этом случае. – Sam

+0

Что заставляет вас думать, что у вас слишком много задач? 'Parallel.ForEach' автоматически отключится по умолчанию. –

+0

Вы можете считать мои задачи рабочими (которые работают параллельно) и в каждом из рабочих, Parallel.For имеет операции ввода-вывода и процессора. Я нашел это расширение VS «Concurrency Visualizer для Visual Studio 2013», которое, если оно работает, как он обещает, должно помочь мне в мониторинге использования ресурсов между прочим. – Sam

0

Эти async методы не создавать новые темы. Они используют потоки из ThreadPool (по умолчанию, вы можете указать иначе). Если вы хотите знать, сколько потоков, что имеет бассейн вы можете использовать ThreadPool.GetMaxThreads:

int workerThreads; 
int completionPortThreads; 
ThreadPool.GetMaxThreads(out workerThreads, out completionPortThreads); 
Console.WriteLine(workerThreads); 

Когда GetMaxThreads возвращается, переменная определяется workerThreads содержит максимальное количество рабочих потоков, разрешенных в пуле потоков, а переменная указанный в файле completionPortThreads содержит максимальное количество асинхронных потоков ввода-вывода, разрешенных в пуле потоков.

Вы можете использовать метод GetAvailableThreads для определения фактического количества потоков в пуле потоков в любой момент времени.

О степени параллелизма, оптимальное количество для действительно процессора интенсивной работы, как правило, количество логических ядер машина, потому что больше потоков будут просто увеличить избыточное переключение контекста

+0

'async' методы планируют все продолжения, однако' SynchronizationContext' говорит, что он должен. Это приложение просто имеет контекст синхронизации по умолчанию, который планирует работу в пуле потоков. Его можно определить как угодно. – Servy

+0

Я понимаю, что мы можем получить доступное количество потоков ThreadPool в любой момент времени, но мой вопрос заключается в том, как я знаю, как «уже назначенные» потоки используют циклы CPU. Если они используют 90% CPU, мне нужно ограничить количество одновременных задач. – Sam

0

TPL не создает потоки, так как вы запускаете задачи, это делает ThreadPool.

Предполагается, что существует 4 системных потока на 4-процессорной машине.На 4-процессорных машинах с четырьмя процессорами запланировано максимум 4 потока, на которых запланировано время, но число объектов потоков в системе намного выше. Эти объекты дороги для создания и обслуживания.

Другие потоки пула потоков будут созданы на случай, если ваши потоки будут спать, делая блокирующий вызов, а использование ЦП ненасыщено. Затем, если вы поставили в очередь элементы ThreadPool, это делает его лучше насыщать, создавая больше потоков.

Если вы хотите, чтобы ThreadPool фактически создавал больше потоков в тесте, вы должны использовать что-то насыщенное ЦП, например while(counter-- > 0);. Только тогда вы получите больше созданных потоков. await Task.Delay(2000); не требуется больше потоков для обработки, потому что ничто не помещается в очередь ThreadPool, и в пуле всегда достаточно потоков, ожидающих обработки очереди.

Если вы делаете это правильно, есть вероятность, что все ваши задачи, не связанные с процессором (например, обработка с помощью i-ой), обрабатываются на одном или двух потоках, что хорошо, и на самом деле является целью async обработка.

После кода ресурсоемкие, и всегда печатает одинаковое количество управляемых потоков как подсчет процессора на моем компьютере:

static void Main(string[] args) 
    { 
     RunTasksWithDelays(); 
    } 

    static void RunTasksWithDelays() 
    { 
     var s = Stopwatch.StartNew(); 
     var tasks = Enumerable.Range(0, 50).Select(i => LongRunningTask()).ToArray(); 

     // Don't need explicit wait, .Result does it effectively. 
     Console.WriteLine(tasks.SelectMany(t => t.Result).Distinct().Count()); 
     Console.WriteLine(s.Elapsed); 
     Console.WriteLine(Environment.ProcessorCount); 
    } 

    static async Task<List<int>> LongRunningTask() 
    { 
     await Task.Yield(); // Force task to complete asyncronously. 
     var threadList = new List<int> {Thread.CurrentThread.ManagedThreadId}; 
     var count = 200000000; 
     while (count-- > 0) ; 
     threadList.Add(Thread.CurrentThread.ManagedThreadId); 
     return threadList; 
    } 
+0

Это всего лишь пример, который я привел. На самом деле, я использую Parallel.ForEach в каждой из этих задач, которая порождает множество потоков. И это вывод вашего кода на моей машине. Количество управляемых потоков = 5 Elapsed = 00: 00: 26.0871327 ProcessorCount = 4 – Sam

Смежные вопросы