Как выбрать между различными способами создания потоков в Delphi? - PullRequest
62 голосов
/ 06 августа 2010

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

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

  1. Класс TThread в Delphi

  2. AsyncCalls от Андреас Хаусладен

  3. OmniThreadLibrary от Primoz Gabrijelcic (габр)

  4. ... другие?


Edit:

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

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

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

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

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

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

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


Вывод:

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

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

Ответы [ 5 ]

43 голосов
/ 06 августа 2010

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

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

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

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

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

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

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

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

7 голосов
/ 06 августа 2010

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

Миши Чарретта.

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

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

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

6 голосов
/ 09 августа 2010

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

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

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

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

С OTL у меня работает комбинация поиска примеров и исходного кода по ключевым словам и задавания вопросов на форумах. Я знаком с традиционными задачами многопоточности "разгрузка ЦП", но сейчас я работаю над созданием фоновой кучи работы с базой данных, в которой гораздо больше "блоков потоков, ожидающих БД" и меньше "ЦП с максимальной нагрузкой", и OTL работает довольно хорошо для этого. Основные отличия состоят в том, что у меня может быть 30+ потоков, работающих без максимальной загрузки ЦП, но остановка одного из них обычно невозможна.

6 голосов
/ 06 августа 2010

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

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

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

1 голос
/ 20 апреля 2011

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

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

РЕДАКТИРОВАТЬ На самом деле Роб Кеннеди отлично справляется с объяснением BeginThread здесь Структура BeginThread - Delphi

РЕДАКТИРОВАТЬ на самом деле, как Роб Кеннеди объясняет TThread в том же посте, я думаю, что я изменю свой код для использования TThread завтра. Кто знает, как это будет выглядеть на следующей неделе! (Возможно AsyncCalls)

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...