Как выбрать между различными способами создания потоков в Delphi?

56

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

Может кто-нибудь объяснить, в чем разница между ними и почему я бы выбрал один за другим.


Edit:

Я только что прочитал прекрасную статью Габра в выпуске журнала под названием "Четыре способа Создать поток". Вам нужно подписаться на получение контента в журнале, поэтому авторским правом я не могу воспроизвести здесь ничего существенного.

В общем, Gabr описывает разницу между использованием TThreads, прямыми вызовами Windows API, Andy AsyncCalls и его собственной OmniThreadLibrary. В заключение он делает вывод, что:

"Я не говорю, что вам нужно выбрать что-нибудь еще, чем классический способ Delphi (TThread), но все же хорошо знать, какие параметры у вас есть"

Ответ Mghie очень тщательный и предлагает OmniThreadLibrary может быть предпочтительнее. Но меня все еще интересуют все мнения о том, как я (или кто-либо) должен выбрать метод их потоков для их применения.

И вы можете добавить в список:

. 4. Прямые вызовы в Windows API

. 5. Миша Шарретт Распределенная прикладная структура CSI, как было предложено LachlanG в его ответе.


Вывод:

Я, вероятно, собираюсь пойти с OmniThreadLibrary. Мне нравится работа Габра. Я использовал его Profiler GPProfile много лет назад, и теперь я использую его GPStringHash, который на самом деле является частью OTL.

Мое единственное беспокойство может заключаться в его обновлении для работы с обработкой 64-разрядных или Unix/Mac, когда Embarcadero добавляет эту функциональность в Delphi.

Теги:
multithreading
asynchronous

5 ответов

39
Лучший ответ

Если вы не испытываете многопоточности, вам, вероятно, не стоит начинать с TThread, поскольку это всего лишь тонкий слой поверх собственной потоковой передачи. Я считаю, что это тоже немного грубо по краям; он не сильно изменился со времени появления с Delphi 2, в основном, изменения, обеспечивающие совместимость Linux в временном интервале Kylix, и исправление более очевидных дефектов (например, исправление сломанного класса MREW и, наконец, отказ от Suspend() и Resume() в последней версии Delphi).

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

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

AsyncCalls - это IMO, которая не очень хороша для фоновых работников, которые остаются в течение всего времени выполнения программы, и это может быть невозможно использовать, когда некоторые из объектов в вашей программе имеют сходство потоков (например, соединения с базой данных или объекты OLE, которые могут иметь что все вызовы происходят в одном потоке).

То, что вам также нужно знать, это то, что эти асинхронные действия не относятся к типу "огонь и забухание". Каждая перегруженная функция AsyncCall() возвращает указатель интерфейса IAsyncCall, который может потребоваться сохранить ссылку, если вы хотите избежать блокировки. Если вы не сохраните ссылку, то момент, когда число ссылок достигнет нуля, интерфейс будет освобожден, что приведет к тому, что поток, освобождающий интерфейс, ждет завершения асинхронного вызова. Это то, что вы можете увидеть во время отладки, когда выход из метода, создавшего IAsyncCall, может занять загадочное количество времени.

OTL - это, на мой взгляд, самый универсальный из ваших трех вариантов, и я бы использовал его без второй мысли. Он может делать все TThread и AsyncCalls, и многое другое. Он имеет звуковой дизайн, который достаточно высок, чтобы облегчить жизнь для пользователя и позволить порту Unixy (при сохранении большей части интерфейса неповрежденным) выглядеть как минимум возможным, если не легко. В последние месяцы он также начал получать некоторые высокоуровневые конструкции для параллельной работы. Настоятельно рекомендуется.

В OTL также есть несколько десятков образцов, что важно для начала. AsyncCalls имеет только несколько строк в комментариях, но тогда ее достаточно легко понять из-за ограниченной функциональности (она делает только одну вещь, но она делает это хорошо). TThread имеет только один образец, который на самом деле не изменился за 14 лет и в основном является примером того, как не делать ничего.

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

  • 3
    Отличный ответ. Я только начинаю исследовать добавление потоков в мое приложение, в котором время от времени случаются затяжные разговоры с моей базой данных, и я действительно хочу, чтобы оно перестало сидеть там, как оратор, который потерял свою ветку. Мне нужен был только такой обзор.
6

(извините, у меня недостаточно очков для комментариев, поэтому я помещаю это как ответ, а не другой голос за OTL)

Я использовал TThread, CSI и OmniThread (OTL). Обе библиотеки имеют нетривиальные кривые обучения, но гораздо более эффективны, чем TThread. Я пришел к выводу, что если вы собираетесь делать что-либо значимое в потоковом режиме, вы в конечном итоге написали половину функциональности библиотеки, так что вы могли бы начать с рабочей, отлаженной версии, которую кто-то написал. И Миша, и Габр являются лучшими программистами, чем большинство из нас, поэтому вероятность того, что они сделали лучшую работу, чем мы.

Я посмотрел на AsyncCalls, но он не делал достаточно того, что я хотел. Одна вещь, которую он имеет, это функция "Синхронизировать" (отсутствует в OTL), поэтому, если вы зависите от того, что вы можете пойти с AynscCalls исключительно для этого. IMO, использующий передачу сообщений, не является достаточно сложным, чтобы оправдать гадость Синхронизации, поэтому пристегнитесь и научитесь использовать сообщения.

Из трех я предпочитаю OTL, в основном из-за коллекции примеров, но также и потому, что он более автономный. Это меньше проблемы, если вы уже используете JCL или работаете только в одном месте, но я делаю микс, включая контрактную работу, и продажа клиентов при установке системы Misha сложнее, чем OTL, только потому, что OTL составляет ~ 20 файлов в одном каталоге. Это звучит глупо, но это важно для многих людей.

В OTL для меня работает комбинация поиска примеров и исходного кода для ключевых слов и задание вопросов на форумах. Я знаком с традиционными задачами по загрузке многопользовательских задач с нагрузкой, но сейчас я работаю над созданием кучи работы с базой данных, которая имеет гораздо больше "блоков потоков, ожидающих DB" и меньше "CPU maxed out", и OTL работает для этого достаточно хорошо. Основные отличия в том, что у меня может быть 30+ потоков без максимальной загрузки CPU, но остановить их вообще невозможно.

6

Существует еще одна менее известная библиотека потоковой обработки Delphi, Misha Charrett CSI Application Framework.

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

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

Миша развивал это в течение многих лет и все еще активно совершенствует структуру и документацию все время. Он всегда очень отзывчив на вопросы.

6

TThread - это простой класс, который инкапсулирует поток Windows. Вы создаете класс потомков с помощью метода Execute, который содержит код, который должен выполнять этот поток, создайте поток и установите его для запуска и выполнения кода.

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

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

1

Я знаю, что это не самый продвинутый метод:-), и, возможно, у него тоже есть ограничения, но я просто попробовал System. BeginThread и нашел это довольно простым - возможно, из-за качества документация Я имел в виду... http://www.delphibasics.co.uk/RTL.asp?Name=BeginThread (ИМО Нейл Моффатт мог чему-то научить MSDN)

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

EDIT На самом деле Роб Кеннеди отлично справляется с описанием BeginThread здесь BeginThread Structure - Delphi

EDIT на самом деле, как Роб Кеннеди объясняет TThread в том же сообщении, я думаю, что я изменю свой код, чтобы использовать TThread tommorrow. Кто знает, как это будет выглядеть на следующей неделе! (Возможно, AsyncCalls)

  • 0
    Я полностью не согласен. Трудно представить худшее, что можно показать новичку, чем вызывать функции графического интерфейса VCL вне основного потока. Код также не устанавливает IsMultiThread . Если после этого кто-то моделирует свой собственный код, то на этапе устанавливается немало трудных для отслеживания ошибок.
  • 0
    @ mghie, вы ссылаетесь на пример на странице Основы Delphi. Ну, я просто посмотрел на это как на пример, как начать отдельную тему. Я не думал об этом как об обязательной поддержке операций GUI из потока. Я рад, что наткнулся на это. TThread, AsyncCalls или OTL заняли бы у меня пару часов дольше (по крайней мере), чтобы достичь того, чего я хотел (1: простое введение в многопоточность и 2: рабочий фрагмент кода, чтобы я мог продолжить с остальными моя работа, а не тратить весь день возиться с нитками!). Теперь я могу учить других в своем собственном темпе, без срочности :-)
Показать ещё 6 комментариев

Ещё вопросы

Сообщество Overcoder
Наверх
Меню