2017-02-27 12 views
0

誰でもこのコードが私のアプリケーションの応答を停止させる理由を教えてもらえますか?ロックにつながるタイマー

私のアプリケーションはCOMライブラリを呼び出します。私はCOMライブラリのイベントが発射されるのを待っています。 私はCOMライブラリは解雇かどうかをチェック維持するためにタイマーを使用します。

procedure MyTimer(hWnd: HWND; uMsg: Integer; idEvent: Integer; dwTime: Integer); stdcall; 
begin 
    //writeln('Timer Event'); 
end; 

イベントがこのように解雇場合、私はチェックしておく:

procedure MyClass.Loop(bwait: boolean); 
var 
s: TDateTime; 
id: uint; 
begin 
    try 
    id := SetTimer(0, 1, 1000, @MyTimer); 
    s := Now; 
    while bwait do 
    begin 
     sleep(30); 
     Application.ProcessMessages; 
     if bwait = false then // Event fired, all good=> exit 
     begin 
     KillTimer(0, id); 
     break; 
     end; 

     if Now - s > EncodeTime(0, 0, 1000, 0) then // Timed out=> exit 
     begin 
     KillTimer(0, id); 
     break; 
     end; 
    end; 

    except 
    on E: Exception do 
     Writeln(E.ClassName, ': ', E.Message); 
    end; 
end; 

COMライブラリのイベントは、それがBWAITブール変数を設定し、発射それはすべての良いことを意味し、私たちは出て行くことができます。

イベントが一定の時間内に発生しなかった場合は、私は&を終了してユーザーに通知します。

このコードでは、スレッドロックが作成されることがあります。

私のアプリケーションとCOMライブラリが応答を停止します。 ロックの原因は何ですか?

上記コードはどのように改善できますか?

ありがとうございます。

+1

COMライブラリ、スレッドモデルなどの知識がなければ、それは言うことができません。確かに、問題のコードは、実際には、 'Sleep'と' Application.ProcessMessages'の呼び出しから非常に疑わしいと思われます。 –

+0

アプリケーションでスレッドを使用していません。 COMライブラリはスレッドを内部的に使用していますが、わかりません。 COMライブラリイベントによってbwait変数が設定されているかどうかを確認するためにApplication.ProcessMessagesは必要ありませんか? 私はスリープ&Application.ProcessMessagesなしのコードを試します。 ありがとう –

+2

非同期モデルを使用して同期コードを書き込もうとしないでください。イベントを使用することの全体的なポイントは、***あなたが他に何をしても***続けることになっているということです。発生したイベントを処理します。取り消しをしようとすると、すべてのメリットが失われ、特定のイベントを受け取るまでコードをブロックする必要があります。受け取らないとどうなりますか? _ロックはロックアップ!_ –

答えて

1

イベントの全目的は、同期ブロッキングコードを書き込まないことです。

Application.ProcessMessages()は、COMメッセージを処理するためのものではありません。代わりにTEventを使用できます。UseCOMWaitパラメータを使用すると、TEvent.WaitFor()メソッドでは、内部的にCoWaitForMultipleHandles()を使用して、イベントが通知されるのを待ってCOMメッセージループを処理します。

uses 
    ..., DateUtils, SyncObjs; 

type 
    MyClass = class 
    private 
    doneEvent: TEvent; 
    procedure COMEventHandler(parameters); 
    procedure Loop(bWait: Boolean); 
    ... 
    public 
    constructor Create; 
    destructor Destroy; override; 
    procedure DoIt; 
    end; 

constructor MyClass.Create; 
begin 
    inherited; 
    ... 
    doneEvent := TEvent.Create(True); 
end; 

destructor MyClass.Destroy; 
begin 
    ... 
    doneEvent.Free; 
    inherited; 
end; 

procedure MyClass.COMEventHandler(parameters); 
begin 
    doneEvent.SetEvent; 
end; 

procedure MyClass.Loop(bWait: Boolean); 
var 
    s: TDateTime; 
    id: UINT; 
begin 
    try 
    doneEvent.ResetEvent; 
    s := Now; 

    while bWait do 
    begin 
     case doneEvent.WaitFor(30) of 
     wrSignaled: begin 
      // Event fired, all good=> exit 
      Break; 
     end; 
     wrTimeout: begin 
      if SecondsBetween(Now, s) > 1000 then 
      begin 
      // Timed out=> exit 
      Break; 
      end; 
      if GetQueueStatus(QS_ALLINPUT) <> 0 then 
      Application.ProcessMessages; 
     end; 
     wrError: begin 
      RaiseLastOSError(doneEvent.LastError); 
     end; 
     end; 
    end; 
    except 
    on E: Exception do 
     Writeln(E.ClassName, ': ', E.Message); 
    end; 
end; 

procedure MyClass.DoIt; 
begin 
    // invoke COM function that will eventually trigger the COM event... 
    Loop(True); 
end; 

しかし、これはイベントドリブンコードを書き込む正しい方法ではありません。他の非同期システムと同様に、コードを細かく分割し、これらのピースを呼び出すタイミングをイベントに通知する必要があります。ブロッキングコードはまったく書き込まないでください。たとえば、

const 
    APPWM_COM_EVENT_DONE = WM_APP + 1; 
    APPWM_COM_EVENT_TIMEOUT = WM_APP + 2; 

type 
    MyClass = class 
    private 
    MsgWnd: HWND; 
    procedure COMEventHandler(parameters); 
    procedure WndProc(var Message: TMessage); 
    public 
    constructor Create; 
    destructor Destroy; override; 
    procedure DoIt; 
    end; 

constructor MyClass.Create; 
begin 
    inherited; 
    MsgWnd := AllocateHWnd(WndProc); 
end 

destructor MyClass.Destroy; 
begin 
    KillTimer(MsgWnd, 1); 
    DeallocateHWnd(MsgWnd); 
    inherited; 
end; 

procedure MyClass.COMEventHandler(parameters); 
begin 
    KillTimer(MsgWnd, 1); 
    PostMessage(MsgWnd, APPWM_COM_EVENT_DONE, 0, 0); 
end; 

procedure MyTimer(hWnd: HWND; uMsg: UINT; idEvent: UINT_PTR; dwTime: DWORD); stdcall; 
begin 
    KillTimer(hWnd, idEvent); 
    PostMessage(hWnd, APPWM_COM_EVENT_TIMEOUT, 0, 0); 
end; 

procedure MyClass.WndProc(var Message: TMessage); 
begin 
    case Message.Msg of 
    APPWM_COM_EVENT_DONE: 
    begin 
     // Event fired, all good 
    end; 

    APPWM_COM_EVENT_TIMEOUT: 
    begin 
     // Event timed out 
    end; 

    else 
    begin 
     Message.Result := DefWindowProc(MsgWnd, Message.Msg, Message.WParam, Message.LParam); 
    end; 
    end; 
end; 

procedure MyClass.DoIt; 
begin 
    SetTimer(MsgWnd, 1, 1000 * 1000, @MyTimer); 
    // invoke COM function that will eventually trigger the COM event... 
end; 
関連する問題