2011-04-08 2 views
11

Приложение написано на Delphi XE.Синхронизация/отправка данных между потоками

У меня есть два класса: TBoss и TWorker, которые основаны на TThread. TBoss - это поток одного экземпляра, который запускается, а затем создает около 20 потоков TWorker.

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

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

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

Есть ли способ вызвать Syncronize в рабочем состоянии, чтобы ждать только потока Boss?

Мой код:

type 
     TWorker = class(TThread) 
     private 
     fResult : TResultRecord; 
     procedure SetOnSendResult(const Value: TNotifyEvent); 
     .... 
     .... 
     public 
     property OnSendResult: TNotifyEvent write SetOnSendResult; 
     property Result : TResultRecord read fResult; 
     .... 
    end; 

    ... 
    ... 
    procedure TWorker.SendBossResults; 
    begin 
     if (Terminated = False) then 
     begin 
     Synchronize(SendResult); 
     end; 
    end; 

    procedure TWorker.SendResult; 
    begin 
     if (Terminated = false) and Assigned(FOnSendResult) then 
     begin 
     FOnSendResult(Self); 
     end; 
    end; 

Тогда в моей Boss нить я сделаю что-то вроде этого

var 
     Worker : TWorker; 
    begin 
     Worker    := TWorker.Create; 
     Worker.OnTerminate := OnWorkerThreadTerminate; 
     Worker.OnSendResult := ProcessWorkerResults; 

Так что мой босс, то есть метод, называемый ProcessWorkerResults - это то, что можно работать на Синхронизировать (SendResult); рабочего.

procedure TBoss.ProcessWorkerResults(Sender: TObject); 
    begin 
     if terminated = false then 
     begin 
     If TWorker(Sender).Result.HasRecord then 
     begin 
      fResults.Add(TWorker(Sender).Result.Items); 
     end; 
     end; 
    end; 
+1

какая версия Delphi есть (есть некоторые новые варианты с 2009 года)? – mjn

+0

Delphi XE - спасибо – Wizzard

+1

Я бы сказал, что использование сообщений для передачи данных между потоками, которое не требует «механизма синхронизации приложений» (оно находится в потоковой «структуре»), гораздо более элегантно, чем использование примитивов синхронизации вокруг общих данных. – Misha

ответ

9

Synchronize специально разработан для выполнения кода в основных резьба; поэтому, кажется, все заперто.

Вы можете использовать несколько способов общаться с рабочими потоками к боссу теме:

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

  • Публикация сообщения от рабочей нити к потоку босса с использованием PostThreadMessage. Недостатком является то, что нить босса должна иметь ручку окна (см. Classes.AllocateHWnd в Справка Дельфы и комментарий Дэвида Хеффернана ниже).

  • Использовать стороннее приложение . См. OmniThreadLibrary - это бесплатно, ОС, и очень хорошо написано.

Мой выбор будет третьим. Primoz сделал для вас всю тяжелую работу. :)

После вашего комментария, вот что-то похожее на первое предложение. Обратите внимание, что это непроверено, так как написав код для темы TBoss и TWorker + тестовое приложение немного длиннее времени, когда у меня есть право в эту минуту ... Наверное, должно быть достаточно, чтобы дать вам суть.

type 
    TWorker = class(TThread) 
    private 
    fResult : TResultRecord; 
    fListIndex: Integer; 
    procedure SetOnSendResult(const Value: TNotifyEvent); 
    .... 
    .... 
    public 
    property OnSendResult: TNotifyEvent write SetOnSendResult; 
    property Result : TResultRecord read fResult; 
    property ListIndex: Integer read FListIndex write FListIndex; 
    .... 
    end; 

type 
    TBoss=class(TThread) 
    private 
    FWorkerList: TThreadList; // Create in TBoss.Create, free in TBoss.Free 
    ... 
    end; 

procedure TWorker.SendBossResults; 
begin 
    if not Terminated then 
    SendResult; 
end; 

procedure TBoss.ProcessWorkerResults(Sender: TObject); 
var 
    i: Integer; 
begin 
    if not terminated then 
    begin 
    If TWorker(Sender).Result.HasRecord then 
    begin 
     FWorkerList.LockList; 
     try 
     i := TWorker(Sender).ListIndex; 
     // Update the appropriate record in the WorkerList 
     TResultRecord(FWorkerList[i]).Whatever... 
     finally 
     FWorkerList.UnlockList; 
     end; 
    end; 
    end; 
end; 
+0

Будьте осторожны с Classes.AllocateHWnd, который не является потокобезопасным. Вам необходимо сериализовать вызовы AllocateHWnd и DeallocateHWnd. Я понятия не имею, почему VCL этого не делает, и в моем коде я использую эти процедуры для обеспечения безопасности потоков. –

+0

@ Давид: Я знал это. :(Я должен был специально упомянуть об этом как о другом недостатке. Спасибо за исправление. –

+0

Привет, да, похоже, это то, что вызывает мою проблему. Однако первым решением является «Добавить обратный вызов для каждого рабочего потока», что в основном является тем, что Я сделал, так как он отличается? – Wizzard

8

Вы можете использовать поточную безопасную очередь. В DelphiXE есть TThreadedQueue. Если у вас нет DXE, попробуйте OmniThreadLibray - эта библиотека очень хороша для всех проблем с потоками.

+0

У TThreadedQueue есть проблемы в многопоточной среде с множеством потребителей или многих производителей. Вместо этого используйте более легкую очередь. См. [Link] (http://www.pascalgamedevelopment.com/showthread.php?4961-freepascal-Delphi-thread-safe-queue) –

+0

Я не уверен, как это отвечает на вопрос «Есть ли способ вызвать Syncronize в рабочем, чтобы только ждать поток Boss?» - Вы можете объяснить? –

1

Как я уже упоминал новые возможности в Delphi 2009 и выше, вот ссылка на пример для Producer/Consumer связи между потоками, на основе новых objct замков, в моем блоге:

Thread Synchronization with Guarded Blocks in Delphi

В note regarding the deprecated methods TThread.Suspend и TThread.Resume, The Embarcadero DocWiki для Delphi рекомендует, чтобы «нить методы синхронизации должны быть на основе SyncObjs.TEvent и SyncObjs.TMutex. «Существует, однако, другой класс синхронизации , доступный с Delphi 2009: TMonitor. Он использует блокировку объекта, который был введен в этой версии ...

+0

Почему TMutex был бы предпочтительнее критического раздела? Кажется странным для меня. –

0

public свойства TWorker класса должны иметь get и set методы, так что вы можете использовать Tcriticalsection дать значения свойств. В противном случае у вас будут проблемы с потоками. Ваш пример выглядит нормально, но в реальном мире тысячи потоков, обращающихся к одному и тому же значению, приведут к ошибке чтения. Используйте критические разделы .. и вам не нужно будет использовать синхронизацию. Таким образом, вы избегаете перехода в очереди сообщений окон и повышения производительности. Кроме того, если вы используете этот код в приложении для Windows-приложений (там, где не разрешены сообщения Windows), этот пример не будет работать. Метод синхронизации не работает, если нет доступа к очереди сообщений Windows.

+1

Службы Windows У Windows есть сообщения Windows. Все приложения Windows состоят из насоса сообщений в их ядре –

0

Решенный! (ответ взята с вопроса)
Исправления, сделанные для этой проблемы, где два раза.
Сначала удалите вызов syncronization в методе TWorker SendBossResult.

Second add fProcessWorkerResult CritialSection для класса TBoss. Создайте и освободите это для создания/уничтожения TBoss. В методе ProcessWorkerResults вызовите fProcessWorkerResult.Enter и fProcessWorkerResult. Оставьте код, который должен быть безопасным из нескольких потоков результатов работника.

Вышеупомянутый вывод был сделан после кода Kens и последующего комментария. Большое спасибо, сэр, шлемы!