Самостоятельная приостановка потока в Delphi, когда он не нужен и безопасно возобновляется - PullRequest
6 голосов
/ 09 декабря 2010

Этот вопрос касается Delphi и XE, в частности, осуждающих Suspend и Resume.Я читал другие посты и пока не нашел аналогичного использования, поэтому я собираюсь пойти дальше и попросить обсудить.

Что я хотел бы знать, так это лучший способ сделать паузупоток, когда он не нужен?

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

В рамках процесса выполнения потока объект извлекается из очереди и обрабатывается в потоке.Обычно это делается для поиска в базе данных.

В конце процесса свойство объекта обновляется и помечается как доступное для основного потока или передается в другую очередь.Последний (ну, на самом деле, это первый) шаг процесса Execute - проверить, есть ли еще какие-либо элементы в очереди.Если он есть, он продолжается, в противном случае он приостанавливает сам себя.

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

Функция возобновления выглядит примерно так.

process TthrdQueue.MyResume();
  begin
    if Suspended then begin
      Sleep(1); //Allow thread to suspend if it is in the process of suspending
      Resume();
    end;
  end;

Выполнение выглядит примерно так:

process TthrdQueue.Execute();
  var
    Obj : TMyObject;
  begin
    inherited;
    FreeOnTerminate := true;
    while not terminated do begin
      if not Queue.Empty then begin
        Obj :=  Pop();
        MyProcess(Obj);  //Do work
        Obj.Ready := true;
      end
      else
        Suspend();  // No more Work
    end;   //Queue clean up in Destructor
  end;  

Функция TthrdQueue Push вызывает MyResume после добавления другого объекта в стек.MyResume вызывает Resume только в том случае, если поток приостановлен.

При завершении работы мы устанавливаем для terminate значение true и вызываем MyResume, если он приостановлен.

Ответы [ 3 ]

6 голосов
/ 09 декабря 2010

Я бы порекомендовал следующую реализацию TthrdQueue:

type
  TthrdQueue = class(TThread)
  private
    FEvent: THandle;
  protected
    procedure Execute; override;
  public
    procedure MyResume;
  end;

implementation

procedure TthrdQueue.MyResume;
begin
  SetEvent(FEvent);
end;

procedure TthrdQueue.Execute;
begin
  FEvent:= CreateEvent(nil,
                       False,    // auto reset
                       False,    // initial state = not signaled
                       nil);
  FreeOnTerminate := true;
  try
    while not Terminated do begin
      if not Queue.Empty then begin
        Obj :=  Pop();
        MyProcess(Obj);  //Do work
        Obj.Ready := true;
      end
      else
        WaitForSingleObject(FEvent, INFINITE);  // No more Work
    end;
  finally
    CloseHandle(FEvent);
  end;
end;
3 голосов
/ 09 декабря 2010

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

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

3 голосов
/ 09 декабря 2010

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

У вас есть много вариантов ожидаемых объектов, включая события, объекты мьютекса, семафоры, очереди сообщений, каналы.

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

Я предпочел использовать технику - использовать очередь сообщений ОС. Я бы заменил ваш объект очереди сообщениями. Затем напишите стандартный цикл GetMessage. Когда очередь пуста, она автоматически блокируется в ожидании нового сообщения. Превратите запрос на прекращение в просто другое сообщение. (Метод TThread.Terminate просто не очень полезная функция, когда вы начинаете делать что-то интересное с потоками, потому что он не виртуальный.)

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