Доступ к переменной в родительской форме из события OnTimer - получение исключения - PullRequest
2 голосов
/ 01 февраля 2009

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

Мой первый вопрос: как я могу сказать в Delphi 2007, какой код выполняется в каком потоке? Есть ли способ в режиме отладки проверить это, чтобы я мог определить наверняка?

Во-вторых, если мне нужно получить доступ и изменить переменные в родительской форме из другого потока, каков наилучший способ сделать это? Кажется, что иногда Delphi позволяет мне обращаться к этим переменным «неправильно» без исключения, а в других случаях это дает исключение.

Ответы [ 4 ]

5 голосов
/ 01 февраля 2009

Просто чтобы быть уверенным: с одной стороны, вы говорите о событии таймера, с другой - о многопоточности. Это два совершенно разных способа параллельного выполнения кода.

Таймер всегда будет работать в главном потоке. Там должно быть безопасно получить доступ ко всему, что было создано и используется в главном потоке. Фактически, событие таймера может произойти, только когда не запущен другой код основного потока, потому что для обработки сообщения таймера требуется обработчик сообщений приложения. Так что это либо вне кода обработки событий, либо когда один из ваших обработчиков событий вызывает Application.ProcessMessages.

Тема очень отличается от этого. В этом случае код в разных потоках выполняется независимо друг от друга. При работе на многопроцессорной машине (или многоядерном процессоре) даже возможно, что они действительно работают параллельно. У вас может быть несколько проблем, в частности, Delphi VCL (включая Delphi XE и выше) не сохраняет потоки, поэтому вызовы любого класса VCL должны выполняться только из основного потока (есть несколько исключений из это правило).

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

3 голосов
/ 01 февраля 2009

Вы задаете два вопроса, поэтому я отвечу на них двумя.

Ваш первый вопрос об использовании TTimers; они всегда запускаются в главном потоке.

Скорее всего, ваше исключение является нарушением прав доступа.

Если это так, это обычно вызывается одним из следующих:

  • a- ваша родительская форма уже уничтожен, когда ваш TTimer стреляет.
  • b- у вас еще нет ссылки на ваша родительская форма, когда ваш TTimer пожары.

b это просто: просто проверьте, если ваша ссылка ноль .

a сложнее и зависит от того, как вы ссылаетесь на родительскую форму.

Как правило, вы хотите убедиться, что ваша ссылка становится нулевой, когда родитель уничтожается или удаляется.

Если вы ссылаетесь на родительскую форму через глобальную переменную (в этом примере через Form2 ), то вам нужно, чтобы TForm2 сделал переменную Form2 nil , используя событие OnDestroy, например:

unit Unit2;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs;

type
  TForm2 = class(TForm)
    procedure FormDestroy(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  Form2: TForm2;

implementation

{$R *.dfm}

procedure TForm2.FormDestroy(Sender: TObject);
begin
  Form2 := nil;
end;

end.

Если вы используете ссылку на поле родительской формы (например, FMyForm2Reference ), то вам следует добавить метод уведомления, подобный этому:

unit Unit1;

interface

 uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, Unit2;

 type
  TForm1 = class(TForm)
  private
    FMyForm2Reference: TForm2;
  protected
    procedure Notification(AComponent: TComponent; Operation: TOperation); override;
  public
  end;

 var
  Form1: TForm1;

 implementation

{$R *.dfm}

 procedure TForm1.Notification(AComponent: TComponent; Operation: TOperation);
 begin
  inherited Notification(AComponent, Operation);
  if (Operation = opRemove) then
    if (AComponent = FMyForm2Reference) then
      FMyForm2Reference := nil;
 end;

 end.

С уважением,

Jeroen Pluimers

3 голосов
/ 01 февраля 2009

Как я могу сказать в Delphi 2007, какие код работает в каком потоке? Является есть способ в режиме отладки для проверки это так я точно могу определить?

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

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

Во-вторых, если мне нужен доступ и изменить переменные в родительской форме из другая нить, как лучше сделать это? Кажется, иногда Delphi позволяет мне получить доступ к этим переменные "неправильно" без предоставления исключение и в других случаях это делает дать исключение.

Если вы действительно находитесь в другом потоке и обращаетесь к общей переменной, вы можете увидеть все, что происходит, если вы не защищаете этот доступ. Это может работать нормально большую часть времени, или вы получите странные значения. Если вы просто хотите изменить целое число потокобезопасным способом, посмотрите InterlockedIncrement. В противном случае вы можете использовать критическую секцию, мьютекс, монитор ... Для этого в JEDI есть несколько полезных классов в модуле JclSynch.

2 голосов
/ 01 февраля 2009

Вы задаете два вопроса, поэтому я отвечу на них двумя.

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

Поскольку переменная находится в форме, для этого лучше всего использовать метод Synchronize .

Существует отличный пример этого, который поставляется вместе с Delphi, он есть в проекте thrddemo.dpr , где модуль в SortThds.pas имеет этот метод, который показывает, как чтобы использовать это:

procedure TSortThread.VisualSwap(A, B, I, J: Integer);
 begin
  FA := A;
  FB := B;
  FI := I;
  FJ := J;
  Synchronize(DoVisualSwap);
 end;

Удачи,

Jeroen Pluimers

...