Параллельные функции в.Net 4.0

Я пробегался через практичность некоторых новых параллельных функций в.Net 4.0.

Скажите, что у меня есть код как так:

foreach (var item in myEnumerable)
    myDatabase.Insert(item.ConvertToDatabase());

Вообразите myDatabase. Вставка выполняет некоторую работу для вставки в базу данных SQL.

Теоретически Вы могли записать:

Parallel.ForEach(myEnumerable, item => myDatabase.Insert(item.ConvertToDatabase()));

И автоматически Вы получаете код, который использует в своих интересах несколько ядер.

Но что, если myEnumerable может только взаимодействоваться с единственным потоком? Параллельный класс перечислит единственным потоком и только отправит результат рабочим потокам в цикле?

Что, если myDatabase может только взаимодействоваться с единственным потоком? Конечно, не было бы лучше сделать соединение с базой данных на повторение цикла.

Наконец, что, если мой "объект var", оказывается, UserControl или что-то, что должно взаимодействоваться с на потоке UI?

За каким шаблоном разработки я должен следовать для решения этих проблем?

Это смотрит на меня, что переключение к Parallel/PLinq/etc не точно легко, когда Вы имеете дело с реальными приложениями.

12
задан jonathanpeppers 5 May 2010 в 18:26
поделиться

5 ответов

Интерфейс IEnumerable по своей сути не является потокобезопасным. Parallel.ForEach будет автоматически обрабатывать это и распараллеливать только элементы, выходящие из вашего перечисления. (Последовательность всегда будет проходить, по одному элементу за раз, по порядку, но результирующие объекты распараллеливаются.)

Если ваши классы (то есть: T) не могут обрабатываться несколькими потоками, вам не следует пытаться распараллелить эту процедуру. Не каждая последовательность является кандидатом на распараллеливание - это одна из причин, почему это не выполняется автоматически компилятором;)

Если вы выполняете работу, требующую работы с потоком пользовательского интерфейса, это все еще потенциально возможно. Однако вам необходимо проявлять такую ​​же осторожность, как и в любое время, когда вы имеете дело с элементами пользовательского интерфейса в фоновых потоках, и маршалировать данные обратно в поток пользовательского интерфейса. Во многих случаях это можно упростить с помощью нового API TaskScheduler.FromCurrentSynchronizationContext . Я писал об этом сценарии в своем блоге здесь .

12
ответ дан 2 December 2019 в 18:18
поделиться

Это очень хороший вопрос, и ответ на него не является на 100% ясным/четким. Я бы указал вам на эту ссылку от Micrsoft, в ней изложено много деталей относительно КОГДА следует использовать параллельные элементы.

0
ответ дан 2 December 2019 в 18:18
поделиться

Как вы уже догадались, использование преимуществ Parallel.For или Parallel.ForEach требует от вас способности компоновать вашу работу в дискретные единицы (воплощенные вашим лямбда-выражением, которое передается в Parallel.ForEach), которые могут выполняться независимо.

2
ответ дан 2 December 2019 в 18:18
поделиться

Все это законные проблемы, и PLINQ / TPL не пытается их решать. Ваша задача как разработчика - написать код, который может правильно работать при распараллеливании.Нет никакого волшебства, которое компилятор / TPL / PLINQ может сделать для преобразования кода, небезопасного для многопоточности, в потокобезопасный код ... вы должны убедиться, что вы это делаете.

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

В случае, когда TPL передает перечислимый поток нескольким потокам, ваше предположение верно. Последовательность перечисляется в одном потоке, и каждый рабочий элемент затем (потенциально) отправляется в отдельный поток для выполнения действий. Интерфейс IEnumerable по своей сути не потокобезопасен, но TPL скрывает это за вас.

Что действительно помогает PLINQ / TPL, так это управлять тем, когда и как распределять работу по нескольким потокам. TPL определяет наличие нескольких ядер на машине и автоматически масштабирует количество потоков, используемых для обработки данных. Если машина имеет только один ЦП / Ядро, то TPL может выбрать , чтобы не распараллеливать работу. Преимущество для вас, разработчика, заключается в том, что вам не нужно писать два разных пути - один для параллельной логики, другой для последовательной. Однако ответственность по-прежнему лежит на вас, чтобы обеспечить безопасный доступ к вашему коду из нескольких потоков одновременно.

Какому шаблону проектирования мне следует следовать, чтобы решить эти проблемы?

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

Если вы используете .NET 4.0, вам также следует изучить классы коллекций ConcurrentXXX в System.Collections.Concurrent . Здесь вы найдете несколько безблокировочных и мелкозернистых конструкций коллекции блокировок, которые упрощают написание многопоточного кода.

6
ответ дан 2 December 2019 в 18:18
поделиться

здесь много обсуждений в ответах и ​​комментариях: Parallel.For (): Обновить переменную вне цикла .

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

0
ответ дан 2 December 2019 в 18:18
поделиться
Другие вопросы по тегам:

Похожие вопросы: