Использование EnterCriticalSection в потоке для обновления метки VCL - PullRequest
4 голосов
/ 15 марта 2010

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

Как мне обновить update TLabel.Caption из моей процедуры, когда она вызывается потоком?

Если я вызывал InitializeCriticalSection в другом месте, это так же просто, как

  EnterCriticalSection(CritSect);
  GlobalVariable := 'New TLabel.Caption';
  LeaveCriticalSection(CritSect);

А потом в моей главной теме:

  EnterCriticalSection(CritSect);
    Label1.Caption:= GlobalVariable;
  LeaveCriticalSection(CritSect);

Но как мне получить код основного потока для вызова? Поток может использовать SendMessage? Или есть какой-то лучший / более простой способ (.OnIdle может проверить флаг, установленный потоком?)

Спасибо.

Ответы [ 4 ]

5 голосов
/ 15 марта 2010

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

Так что, если вашему потоку необходимо обновить элемент GUI, он должен делегировать это основному потоку. Для этого вы можете использовать разные техники:

Самый простой - использовать метод Synchronize в вашем коде потока. Когда вызывается Synchronize, ваш поток приостанавливается, код, предоставленный вами для Synchronize, будет выполняться в контексте основного потока, а затем ваш поток возобновляется.

Если вам не нравится, когда ваш поток останавливается каждый раз, когда вызывается этот фрагмент кода, вы можете использовать метод Queue. Очередь отправляет ваш запрос в очередь сообщений целевого потока (здесь основной поток), поэтому ваш поток не остановится, но пользовательский интерфейс может обновиться не сразу, в зависимости от того, насколько занята очередь сообщений основного потока.

Другим способом достижения этой цели является отправка пользовательских сообщений Windows в основной поток с помощью функций API SendMessage или PostMessage. В этом случае вы должны определить пользовательское сообщение и отправить его в основной поток всякий раз, когда вам нужно изменить элемент пользовательского интерфейса. Ваш основной поток должен предоставить обработчик сообщений для этого типа сообщений и обрабатывать полученные сообщения. Следствием этого является нечто похожее на использование метода Queue.

5 голосов
/ 15 марта 2010

Чтобы ваш код вызывался в основном потоке, взгляните на TThread.Synchronize. Он принимает указатель метода (или, в D2009 +, анонимный метод) и заботится обо всех скрытых сообщениях, чтобы гарантировать, что ваш код будет выполняться в основном потоке.

4 голосов
/ 15 марта 2010

Вы должны убедиться, что этикетка обновлена ​​безопасным способом. Кроме того, VCL запускается в главном потоке приложения, и связывание с ним других потоков может привести к странным результатам. Даже если используются критические секции.

Так что мой совет: просто используйте PostMessage. Когда вызывается ваша процедура обратного вызова, просто вызовите PostMessage из этой процедуры в дескриптор главного окна формы. Это обеспечит установку заголовка метки в контексте основного потока.

Пример кода:

type
  TForm1 = class(TForm)
  private
    procedure OnWMUpdateLabel(var Msg: TMessage); message WM_UPDATE_LABEL;
    procedure MyCallbackProcedure(const Sender: TObject);
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

procedure TForm1.OnWMUpdateLabel(var Msg: TMessage);
begin
  Label1.Caption := SomeVariable;
end;

procedure TForm1.MyCallbackProcedure(const Sender: TObject);
begin
  SomeVariable := 'New Label';
  PostMessage(Handle, WM_UPDATE_LABEL, 0, 0);
end;

Но вы должны быть осторожны, если проходите по этому пути. Вы должны синхронизировать доступ к такой переменной. Или вы можете использовать GlobalAddAtom (что немного устарело) или что-то подобное.

EDIT:

Как уже сказал Мейсон, вы также можете использовать Синхронизировать, который проще в использовании. Для вашей проблемы все должно быть в порядке.

РЕДАКТИРОВАТЬ 2:

Для справки, как использовать GlobalAddAtom (да, я ошибся в нем ранее):

http://www.delphi3000.com/articles/article_574.asp?SK=

0 голосов
/ 16 марта 2010

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

...