Потоки в Delphi: CriticalSection не является Release'd при использовании Synchronize внутри своего метода - PullRequest
4 голосов
/ 14 ноября 2011

В моем проекте есть Поток, который может быть изменен самим потоком, другим потоком или VCL (основным приложением).Таким образом, я использую TCriticalSection.Acquire / Release для каждого доступа к данным.

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

Должен ли я пропустить синхронизациюметод (даже если код Synchronize вызывает VCL) и полагаться на сам CriticalSection?В чем причина такого поведения?

код основного потока:

  fData:= nil;
  try
    fSingleRequest.Acquire;      
    if fItem <> nil then
      begin
        fData:= fItem.Request;
        SubmitRequest();
        fCallbackData:= fItem.fExtraData;
        fCallback:= fItem.fCallback;
        Synchronize(DoCallback); // <-- this line is called
      end;
  finally
    fSingleRequest.Release;      // <-- this isn't under the specific situation
  end;

1 Ответ

9 голосов
/ 14 ноября 2011

Если ваш так называемый «основной» поток получает критическую секцию, а затем поток VCL (что мы обычно называем «основным» потоком) пытается получить его, тогда VCL поток будет блокироваться, пока критический раздел не будет освобожден. Затем ваш «основной» поток вызывает Synchronize, который выполняет данную функцию в контексте потока VCL. Поскольку поток VCL блокируется в ожидании критической секции, он не обрабатывает сообщения, поэтому он не может заметить, что существует синхронизированный метод для вызова. Таким образом, тупик.

Не удерживать блокировку между вызовами внутри потока. Освободите блокировку в «основном» потоке, прежде чем вызывать Synchronize, а затем повторно запустите ее, если она вам все еще нужна. Если данные, используемые в синхронизированном методе, нуждаются в постоянной защите от одновременного доступа, то я думаю, что вы должны найти способ скопировать данных в отдельный, не общий объект. Пусть синхронизированный метод использует этот объект вместо общего, а затем уничтожит его.

Ваш комментарий означает, что вы можете вызвать функцию обратного вызова без Synchronize, и все, кажется, работает. В этом случае синхронизированный метод не вызывается в том же контексте потока, что и раньше. Он вызывается непосредственно вашим "основным" потоком, а не потоком VCL. Это, очевидно, снимает конфликт блокировки, но действительно ли это безопасно, зависит от того, что делает функция обратного вызова.

...