Альтернатива потокам

Я считал, что потоки очень проблематичны. Какие альтернативы доступны? Что-то, что обрабатывает блокирование и материал автоматически?

Много людей рекомендует второстепенному рабочему, но я понятия не имею почему.

Кто-либо хочет объяснить "легкие" альтернативы? Пользователь сможет выбрать количество потоков для использования (в зависимости от их потребностей скорости и производительности компьютера).

Какие-либо идеи?

5
задан JeffH 5 February 2010 в 22:48
поделиться

9 ответов

Подводя итог проблем с потоками:

  • если потоки совместно используют память, вы можете получить гонку условия
  • , если вы избегаете гонок, обильно используя блокировки, вы можете получить тупиковые ситуации (см. задачу обедающих философов )

Пример гонки: предположим, что два потока имеют общий доступ к некоторая память, где хранится число. Поток 1 считывает адрес памяти и сохраняет его в регистре ЦП. Поток 2 делает то же самое. Теперь поток 1 увеличивает число и записывает его обратно в память. Затем поток 2 делает то же самое. Конечный результат: число увеличивалось только на 1, в то время как оба потока пытались увеличить его. Результат таких взаимодействий зависит от времени. Хуже того, может показаться, что ваш код работает без ошибок, но однажды в синюю луну время не подходит, и случаются плохие вещи.

Чтобы избежать этих проблем, ответ прост: избегайте совместного использования записываемой памяти. Вместо этого используйте передачу сообщений для связи между потоками. Крайний пример - поместить потоки в отдельные процессы и обмениваться данными через TCP / IP-соединения или именованные каналы.

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

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

Я могу порекомендовать этот проект. Бассейн интеллектуальных резьбовых соединений

Описание проекта. Smart Thread Pool - это пул нитей, написанный на C#. Это гораздо более продвинутый, чем .NET встроенный пул потоков. Вот список функций пула потоков:

  • Количество потоков динамически изменяется в зависимости от нагрузки на потоки в пуле.
  • Рабочие элементы могут вернуть значение.
  • Рабочий элемент может быть отменен.
  • Контекст вызывающего потока используется при выполнении (ограниченном) рабочего элемента.
  • Использование минимального количества обработчиков событий Win32, поэтому счетчик хэндлов приложения не взорвется.
  • Звонящий может дождаться завершения нескольких или всех рабочих элементов.
  • Рабочий элемент может иметь обратный вызов PostExecute, который вызывается, как только рабочий элемент будет завершен.
  • Объект состояния, сопровождающий рабочий элемент, может быть утилизирован автоматически.
  • Исключения рабочего элемента отправляются обратно вызывающему абоненту.
  • Рабочие элементы имеют приоритет.
  • Группа рабочих элементов.
  • Абонент может приостановить запуск пула потоков и группы рабочих элементов.
  • Потоки имеют приоритет.
  • Может запускать COM-объекты с однопоточной квартирой.
  • Поддержка действий и функций делегатов.
  • Поддержка WindowsCE (лимитированная)
  • Макс. и мин. потоки могут быть изменены во время выполнения.
  • Поведение отмены импортировано.
1
ответ дан 18 December 2019 в 10:44
поделиться

Не будут ли параметры параллельного программирования в .Net 4 «простым» способом использования потоков? Я не уверен, что бы я предложил для .Net 3.5 и более ранних версий ...

Эта ссылка MSDN на Центр разработчиков параллельных вычислений содержит ссылки на множество информации о программировании Parellel, включая ссылки на видео и т. д.

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

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

Многие инструменты упрощают эту задачу. В .NET есть несколько классов, специально предназначенных для облегчения работы с несколькими потоками, включая класс BackgroundWorker , который значительно упрощает выполнение фоновой работы и взаимодействие с пользовательским интерфейсом.

.NET 4 многое сделает, чтобы еще больше упростить эту задачу. Библиотека параллельных задач и PLINQ значительно упрощают работу с несколькими потоками.

Что касается вашего последнего комментария:

Пользователь сможет выбрать количество используемых потоков (в зависимости от их скорости и мощности компьютера).

Большинство подпрограмм в .NET построены на ThreadPool . В .NET 4 при использовании TPL рабочая нагрузка будет фактически масштабироваться во время выполнения для вас, избавляя вас от необходимости указывать количество используемых потоков. Однако есть способы сделать это сейчас.

В настоящее время вы можете использовать ThreadPool.SetMaxThreads , чтобы ограничить количество генерируемых потоков. В TPL вы можете указать ParallelOptions.MaxDegreesOfParallelism и передать экземпляр ParallelOptions в вашу процедуру для управления этим. Поведение по умолчанию масштабируется с увеличением количества потоков по мере добавления дополнительных ядер обработки, что обычно является лучшим поведением в любом случае.

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

Нитки не проблематичны, если вы понимаете, что вызывает с ними проблемы.

Например, если вы избегаете статики, вы знаете, какой API использовать (например, использовать синхронизированные потоки ), вы избежите многих проблем, которые возникают из-за их плохого использования.

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

Если многопоточность является проблемой (это может произойти, если у вас есть небезопасные/неуправляемые dll сторонних производителей, которые не могут поддерживать многопоточность. В этом случае можно создать meachism для постановки операций в очередь, т.е. хранить параметры действия в базе данных и просто прогонять их по одному. Это можно сделать в службе windows. Очевидно, что это займет больше времени, но в некоторых случаях это единственный вариант.

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

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

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

Отличное место для начала - статья Джо Альбахари: http://www.albahari.com/threading/.

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

«Проблемная» - это не то слово, которое я бы использовал для описания работы с потоками. «Утомительный» - более подходящее описание.

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

Что касается конкретных параметров потоковой передачи в C #, вот несколько советов о том, когда использовать каждый из них.

  • Используйте BackgroundWorker , если у вас есть одна задача, которая выполняется в фоновом режиме и должна взаимодействовать с пользовательским интерфейсом. Задача маршалинга данных и вызовов методов в поток пользовательского интерфейса обрабатывается автоматически с помощью его модели, основанной на событиях. Избегайте использования BackgroundWorker, если (1) ваша сборка еще не ссылается на сборку System.Windows.Form, (2) вам нужно, чтобы поток был потоком переднего плана, или (3) вам нужно управлять приоритетом потока.
  • Используйте поток ThreadPool , когда требуется эффективность. ThreadPool помогает избежать накладных расходов, связанных с созданием, запуском и остановкой потоков. Избегайте использования ThreadPool, если (1) задача выполняется в течение всего времени существования вашего приложения, (2) вам нужно, чтобы поток был потоком переднего плана, (3) вам нужно управлять приоритетом потока или (4) вам нужен поток иметь фиксированную личность (прерывание, приостановка, обнаружение).
  • Используйте класс Thread для длительных задач и когда вам требуются функции, предлагаемые формальной моделью потоковой передачи, например, выбор между приоритетным и фоновым потоками, настройка приоритета потока, детальный контроль над выполнением потока и т. д.
1
ответ дан 18 December 2019 в 10:44
поделиться

Это немного более высокоуровневый ответ, но он может быть полезен, если вы захотите рассмотреть другие альтернативы потоков. В любом случае, в большинстве ответов обсуждаются решения, основанные на потоках (или пулах потоков) или, возможно, задачах из .NET 4.0, но есть еще одна альтернатива, которая называется message-passing. Она успешно использовалась в Erlang (функциональный язык, используемый Ericsson). Поскольку функциональное программирование в наши дни становится все более распространенным (например, F#), я подумал, что могу упомянуть об этом. В genral:

  • Threads (или пулы потоков) обычно могут использоваться, когда у вас есть некоторые относительно длительные вычисления. Когда необходимо разделить состояние с другими потоками, это становится сложным (необходимо правильно использовать блокировки или другие примитивы синхронизации).

  • Задачи (доступно в TPL в .NET 4.0) очень легки - Вы можете разделить свою программу на тысячи задач, а затем дать время ее выполнения (она будет использовать оптимальное количество потоков). Если Вы можете написать свой алгоритм, используя задачи, а не потоки, то это звучит как хорошая идея - Вы можете избежать некоторой синхронизации при выполнении вычислений с меньшими шагами.

  • Деляративные подходы (PLINQ в .NET 4.0 - отличный вариант), если у вас есть какая-либо операция обработки данных более высокого уровня, которая может быть закодирована с помощью LINQ-примитивов, то вы можете использовать эту технику. Время выполнения автоматически распараллелит ваш код, так как в LINQ не указано, как именно должен оцениваться результат (вы просто говорите, какие результаты хотите получить).

  • Message-passing позволяет писать программу как параллельно выполняющиеся процессы, которые выполняют некоторые (относительно простые) задачи и общаются, посылая сообщения друг другу. Это замечательно, потому что Вы можете разделить некоторое состояние (отправить сообщения) без обычных проблем синхронизации (Вы просто отправляете сообщение, затем делаете что-то другое или ждете сообщений). Вот хорошее вступление к передаче сообщений на F# от Роберта Пикеринга.

Обратите внимание, что последние три техники вполне связаны с функциональным программированием - в функциональном программировании вы по-другому относитесь к программам - как к вычислениям, возвращающим результат (что облегчает использование Tasks). Также Вы часто пишете декларативный и более высокоуровневый код (что облегчает использование Declarative approaches).

Когда дело доходит до фактической реализации, F# имеет замечательную библиотеку, передающую сообщения прямо в базовых библиотеках. В C# можно использовать Concurrency & Coordination Runtime, что кажется немного "халтурным", но, наверное, тоже достаточно мощно (но может выглядеть слишком сложно).

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

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