Какие высокоуровневые конструкции .NET 4.5 (или более ранней версии) упрощают многопоточность?

Делегаты - это некоторые из объектов, которые упрощают работу с потоками в справочнике по .NET. Их можно использовать для асинхронного вызова метода. Какие еще объекты существуют в framework 4.5 (или более ранней версии), которые упрощают использование потоков или снижают вероятность ошибок?

Какие еще абстракции упрощают параллелизм и многопоточность?

Примечание. Этот вопрос обновляет это.


person halfbit    schedule 10.05.2012    source источник
comment
Делегаты и события не имеют ничего общего с потоками, и они не относятся к .NET 4.5. Пожалуйста, будьте более ясны, так как я не могу понять, о чем вы говорите.   -  person John Saunders    schedule 10.05.2012
comment
@JohnSaunders Async Delegates действительно упрощает многопоточность; см. msdn.microsoft.com/en-us/library/22t547yb.aspx   -  person halfbit    schedule 10.05.2012
comment
Вы заметили, что асинхронное использование делегатов было в .NET с версии 1.0?   -  person John Saunders    schedule 10.05.2012
comment
@JohnSaunders - Я прояснил вопрос, включив предыдущие версии .NET в надежде получить общую картину. Меня выдвинули на повторное открытие, так как я не вижу уважительных оснований для закрытия, особенно с моими правками / разъяснениями. Наконец, я получил фантастический ответ, как я и надеялся. Его следует снова открыть, чтобы другие могли извлечь выгоду из вклада Брайана.   -  person halfbit    schedule 10.05.2012
comment
Я сделал больше редактирования; чтобы расширить ваш вопрос от единого пространства имен и избежать использования слова «задача», которое в данном контексте перегружено. В результате я проголосовал за повторное открытие.   -  person John Saunders    schedule 11.05.2012
comment
@JohnSaunders Я ценю конструктивный отзыв! Спасибо!   -  person halfbit    schedule 11.05.2012


Ответы (4)


Я обычно отвечаю на множество вопросов, связанных с многопоточностью, и часто вижу, что один и тот же основной вопрос задается разными способами. Я расскажу о наиболее распространенных проблемах, которые я видел на протяжении многих лет, и объясню, как новые технологии облегчили решение этих проблем.

Закрытие переменной цикла

Это не проблема, специфичная для многопоточности, но использование многопоточности определенно усугубляет проблему. C # 5.0 устраняет эту проблему для цикла foreach, создавая новую переменную для каждой итерации. Вам больше не нужно будет создавать специальную переменную для замыканий лямбда-выражений. К сожалению, цикл for все равно нужно будет обрабатывать с помощью специальной переменной захвата.

Ожидание завершения асинхронных задач

.NET 4.0 представил класс CountdownEvent, который инкапсулирует большую часть логики, необходимой для ожидания завершения многих задач. Большинство младших разработчиков использовали Thread.Join звонка или один WaitHandle.WaitAll звонок. У обоих есть проблемы с масштабируемостью. Старый шаблон заключался в использовании одного ManualResetEvent и сигнализировать ему, когда счетчик достигнет нуля. Счетчик обновлен с использованием класса Interlocked. CountdownEvent значительно упрощает этот шаблон. Просто не забудьте рассматривать свой main как рабочий, чтобы избежать этого тонкого состояния гонки, которое может возникнуть, если один рабочий заканчивает работу до того, как все рабочие будут поставлены в очередь.

.NET 4.0 также представил класс Task, который может связывать дочерние задачи с помощью TaskCreationOptions.AttachedToParent. Если вы вызовете Task.Wait для родительского объекта, он также будет ждать завершения всех дочерних задач.

Производитель-Потребитель

.NET 4.0 представил класс BlockingCollection, который действует как обычная очередь, за исключением того, что он может блокироваться, когда коллекция пуста. Вы можете поставить объект в очередь, вызвав Add, и вывести объект из очереди, вызвав Take. Take блокируется, пока элемент не станет доступным. Это значительно упрощает логику производитель-потребитель. Раньше разработчики пытались написать свой собственный класс блокирующей очереди. Но, если вы не знаете, что делаете, вы действительно можете облажаться ... плохо. Фактически, в течение долгого времени у Microsoft был пример очереди блокировки в документации MSDN, которая сама была сильно повреждена. К счастью, с тех пор его удалили.

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

Введение BackgroundWorker значительно упростило выделение фоновой задачи из приложения WinForm для начинающих разработчиков. Основное преимущество заключается в том, что вы можете вызывать ReportProgress из обработчика событий DoWork, и обработчики событий ProgressChanged будут автоматически перенаправлены в поток пользовательского интерфейса. Конечно, любой, кто отслеживает мои ответы на SO, знает, как я отношусь к операциям маршалинга (через Invoke и т.п.) как к решению для обновления пользовательского интерфейса простой информацией о ходе выполнения. Я все время рву его, потому что это вообще ужасный подход. BackgroundWorker по-прежнему вынуждает разработчика использовать модель push (посредством операций маршалинга в фоновом режиме), но, по крайней мере, он делает все это за кулисами.

Неэлегантность Invoke

Все мы знаем, что к элементу пользовательского интерфейса можно получить доступ только из потока пользовательского интерфейса. Обычно это означало, что разработчик должен был использовать операции маршалинга через ISynchronizeInvoke, DispatcherObject или SynchronizationContext для передачи управления обратно потоку пользовательского интерфейса. Но давайте посмотрим правде в глаза. Эти операции маршалинга выглядят некрасиво. Task.ContinueWith сделал это немного более элегантным, но настоящая слава досталась await как части новой модели асинхронного программирования C # 5. await можно использовать для ожидания завершения Task таким образом, что управление потоком временно прерывается во время выполнения задачи, а затем возвращается в этом самом месте в правильном контексте синхронизации. Нет ничего более элегантного и приятного, чем использование await в качестве замены для всех этих Invoke вызовов.

Параллельное программирование

Я часто вижу вопросы о том, как все может происходить параллельно. Старый способ заключался в создании нескольких потоков или использовании ThreadPool. .NET 4.0 давал возможность использовать TPL и PLINQ. Класс Parallel - отличный способ заставить итерации цикла идти параллельно. И AsParallel PLINQ - это другая сторона той же медали для простого старого LINQ. Эти новые функции TPL значительно упрощают эту категорию многопоточного программирования.

.NET 4.5 представляет библиотеку потока данных TPL. Он призван сделать изящную задачу параллельного программирования, которая в остальном была бы сложной. Он разделяет классы на блоки. Это могут быть целевые блоки или исходные блоки. Данные могут перетекать из одного блока в другой. Есть много разных блоков, включая BufferBlock<T>, BroadcastBlock<T>, ActionBlock<T> и т. Д., Которые выполняют разные функции. И, конечно же, вся библиотека будет оптимизирована для использования с новыми ключевыми словами async и await. Это захватывающий новый набор классов, который, я думаю, постепенно завоюет популярность.

Изящное увольнение

Как заставить нить останавливаться? Я часто вижу этот вопрос. Самый простой способ - позвонить Thread.Abort, но мы все знаем об опасности этого ... Я надеюсь. Есть много разных способов сделать это безопасно. .NET 4.0 представила более унифицированную концепцию, называемую отменой через CancellationToken и CancellationTokenSource. Фоновые задачи могут опрашивать IsCancellationRequested или просто вызывать ThrowIfCancellationRequested в безопасных точках, чтобы корректно прервать выполняемую ими работу. Другие потоки могут вызывать Cancel, чтобы запросить отмену.

person Brian Gideon    schedule 10.05.2012
comment
Вау, это как раз то, что мне нужно! Я повсюду искал вид на вещи с высоты в 1000 футов, и вот он. Слишком плохие люди закрыли этот вопрос; Я выставил его на повторное открытие. - person halfbit; 10.05.2012
comment
Должны ли Reactive Extensions (Rx) и StreamInsight также быть в этом списке? - person halfbit; 11.05.2012
comment
@ makerofthings7: Наверное ... когда у меня будет время, я сделаю это. - person Brian Gideon; 11.05.2012
comment
Не забывайте, что поток данных TPL включен в .Net 4.5 и может использоваться в .Net 4.0. Это позволяет обрабатывать сложный поток данных параллельно. - person MuiBienCarlota; 30.10.2012

Что ж, посмотрим здесь:

  1. Класс ThreadPool - довольно старый, но все же надежный для простого шаблона производитель-потребитель.
  2. BackgoundWorker (.NET 2.0+) - еще одна конструкция старой школы, предоставляющая полезные функции для выполнения задач в фоновом режиме в приложениях с графическим интерфейсом.
  3. Timers - полезно для выполнения кода через определенные промежутки времени с использованием фонового потока.
  4. Класс Task (.NET 4.0+) - абстракции потоков, которые работают в базовом пуле потоков и предоставляют множество полезных функций, таких как маршалинг и планирование исключений. Полезно для так называемого шаблона «параллелизма задач».
  5. Parallel.For, Parallel.ForEach (.NET 4.0+) - подходят для параллельного выполнения одной и той же операции над набором данных. Полезно для так называемого шаблона «параллелизм данных».
  6. Parallel.Invoke (.NET 4.0+) - дальнейшая абстракция над Tasks. Просто запускает несколько частей кода (методы, лямбды) параллельно.
  7. Параллельные коллекции (.NET 4.0+) - все, что вам нужно для передачи или обмена данными между потоками эффективным и потокобезопасным способом.
person Tudor    schedule 10.05.2012


Task и Task<T>, но они были здесь с .NET 4. async не обязательно работает с потоками, см. видео Джона от Øredev за очень хорошее объяснение.

person Matthias Meid    schedule 10.05.2012