Подтвердить что ты не робот

Какие конструкторы более высокого уровня .NET 4.5 (или более ранние) упрощают Threading?

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

Каковы другие абстракции, облегчающие concurrency и многопоточность?

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

4b9b3361

Ответ 1

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

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

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

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

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

В .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 как часть новой асинхронной модели программирования С# 5. await может использоваться для ожидания завершения Task таким образом, чтобы управление потоком временно прерывалось во время выполнения задачи и затем возвращалось именно в этом месте в правильном контексте синхронизации. Нет ничего более элегантного и удовлетворяющего, чем использование await в качестве замены для всех этих вызовов Invoke.

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

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

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

Изящное завершение

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

Ответ 2

Хорошо, посмотрим здесь:

  • Класс ThreadPool - своего рода старый, но все же надежный для простого шаблона производителя-потребителя.
  • BackgoundWorker (.NET 2.0+) - еще одна конструкция старой школы, предоставляющая полезные функции для выполнения задач в фоновом режиме в приложениях с графическим интерфейсом.
  • Timer - полезно для выполнения кода через определенные промежутки времени с использованием фонового потока.
  • Task class (.NET 4.0+) - потоковые абстракции, которые выполняются в пуле потоков и предоставляют множество полезных функций, таких как маршалинг и планирование исключений. Полезно для так называемого шаблона "задача parallelism".
  • Parallel.For, Parallel.ForEach (.NET 4.0+) - полезно для выполнения одной и той же операции над набором данных параллельно. Полезно для так называемого "данных parallelism".
  • Parallel.Invoke (.NET 4.0+) - дополнительная абстракция над Task s. Просто запускает несколько фрагментов кода (методы, лямбды) параллельно.
  • Параллельные коллекции (.NET 4.0+) - все, что вам нужно для передачи или совместного использования данных между потоками эффективным и потокобезопасным способом.

Ответ 3

Без сомнения, получение новой библиотеки Tpl DataFlow (включенной в .net 4.5) даст вам самый большой импульс параллельного развития.

Если вы серьезно относитесь к высококонкурентным приложениям, потратьте день или два на знакомство с DataFlow. Это серьезно.

Ответ 4

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