Вопрос

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

TMonitor.Enter (FTCPClient);
try
  WorkerThread := TWorkerThread.Create (SomeLengthyServerOperation);
  while (not WorkerThread.Ready) do
    Application.ProcessMessages;
  DoSometingWithResults (WorkerThread.Result);
  WorkerThread.Free;      
finally
  TMonitor.Exit (FTCPClient);
end;

Workerthread - это простой класс, полученный из tthread, который выполняет функцию, передаваемую его конструктору, а затем завершается (с помощью ready = true и результат в результате). Представленный код выполняется всякий раз, когда нажимается кнопка.

Теперь к моему вопросу: если я нажимаю на кнопку дважды очень быстро, я получаю несколько странных ошибок, которые очень похожи на общение между сервером и клиентом, которые я хотел избежать, заблокировав объект ftcpclient. В каком потоке обрабатыватели событий после приложения. Проведены процедуры? Замок Tmonitor на поток? Означает ли это, что блокировка не работает, если я использую Application.ProcessMessages?

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

РЕДАКТИРОВАТЬ: Для отключения и включения кнопки: я ничего не знаю о клиентском коде. Может быть обработчик событий кнопки, может быть чем -то другим. В основном я хочу скрыть блокировку от клиентского кода.

Это было полезно?

Решение

Tmonitor только блокирует другой Поток от приобретения блокировки. То, что происходит так: обработка сообщений из блокировки вы возвращаетесь к той же функции в том же потоке, что вызывает рекурсивное получение блокировки. Затем ваш код создает новую рабочую ветку и запускает цикл повсюду. Вы можете отключить кнопку, чтобы вы не смогли нажать ее снова, пока не завершится поток работника. Убедитесь, что вы отключите кнопку до Вы начинаете обрабатывать сообщения и используете еще одну попытку. Начально блокируйте, чтобы убедиться, что они будут переработаны. В зависимости от того, как расположена остальная часть вашего кода, вам может даже не понадобиться блокировка.

Другие советы

Некоторые комментарии:

  1. Ваш работник звучит так, будто вы только что переосмыслили Асинккаллс. Анкет Использование проверенной и протестированной реализации, вероятно, лучше, чем писать свою собственную (если вы не делаете это для учебного эффекта или потому, что у вас есть очень особые требования). Пожалуйста, посмотрите на оба Асинккаллс и Omnithreadlibrary.

  2. Блокировка должна происходить только как можно более коротким, поэтому обертка всей реакции на кнопку нажимает на Monitor.Enter () и Monitor.Exit (), кажется неправильным. Также не совсем понятно, в какой цели это служит.

  3. Вызов Application.processmessages () В то время как замок удерживается, может представить все виды неприятных сюрпризов. Если вам нужно удержать свой код от возврата, это, как правило, лучше всего отключить все элементы пользовательского интерфейса, как первое, что делает обработчик Onclick, и восстанавливает их, когда обработчик закончен. Также обратите внимание, что блокировки могут быть введены несколько раз из одного и того же потока, они достигают только эксклюзивного доступа от несколько потоки.

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

  5. Если вы посмотрите на этот код, вы увидите, что вы можете достичь того же самого, выполняя свою работу в потоке графического интерфейса вместо порока рабочей потока.

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

Редактировать: Трудно сказать точно, но ваш код, вероятно, должен быть что -то вроде этого:

procedure TForm1.ActionStartExecute(Sender: TObject);
begin
  ActionStart.Enabled := FALSE;
  fWorkerThread := TWorkerThread.Create (Handle, SomeLengthyServerOperation);
end;

procedure TForm1.ActionStartUpdate(Sender: TObject);
begin
  ActionStart.Enabled := fWorkerThread = nil;
end;

procedure TForm1.WMThreadFinished(var AMsg: TWMThreadFinishedMsg);
begin
  // process results
  fWorkerThread := nil;
end;

TworkerThread Free Fres, но как последнее действие, оно публикует сообщение в форму (поэтому он получает ручку окна в качестве параметра). В обработчике для этого сообщения вы установили FWorkerThread в NIL, чтобы действие было повторно включено в следующем цикле холостого хода. Использование акции означает, что вам не нужно заботиться о том, что возникло элемент пользовательского интерфейса, создание потока - все они будут отключены при создании потока, и будет повторно включен, когда поток закончен. Никакой блокировки не требуется, и никакой новый поток не может быть создан, пока поток активен.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top