Form1에서 WM_MYSHOW 메세지가 오면 'Hello World'를 찍도록 구성을 합니다.


const

  WM_MYSHOW = WM_USER + 10;


  TForm1 = class(TForm)

  // ...

  private

    procedure WmMyShow(var Msg: TMessage); message WM_MYSHOW;

  // ...

  end;


procedure TForm1.WmMyShow(var Msg: TMessage);

begin

  Memo1.Lines.Add('Hello world');

end;






MyThread라는 스레드 클래스를 만듭니다. 하는 일은 1초 대기하고 있다가 Form1에 WM_MYSHOW 메세지를 던져서 화면에 'Hello World'가 찍히도록 하는 겁니다.


  TMyThread = class(TThread)

    procedure Execute; override;

  end;


procedure TMyThread.Execute;

begin

  Sleep(1000);

  SendMessage(Form1.Handle, WM_MYSHOW, 0, 0);

end;






이제 MyThread를 생성해서 사용을 해 보겠습니다. MyThread를 생성하고 2초 경과뒤에 스레드의 해제를 하도록 합니다.

[소스1]

procedure TForm1.Button1Click(Sender: TObject);

var

  MyThread: TMyThread;

begin

  MyThread := TMyThread.Create(false);

  Sleep(2000);

  MyThread.WaitFor; // <--- (1)

  MyThread.Free;

end;


퀴즈 1.  [소스 1]은 Deadlock이 될까요, 아니면 잘 넘어 갈까요?






상기 코드에서 (1)번 부분을 WaitFor 메소드를 사용하지 않고 WaitForSingleObject 라는 API로 변경해서 테스트를 해 보겠습니다.


[소스2]

procedure TForm1.Button2Click(Sender: TObject);

var

  MyThread: TMyThread;

begin

  MyThread := TMyThread.Create(false);

  Sleep(2000);

  WaitForSingleObject(MyThread.Handle, INFINITE); // <--- (2)

  MyThread.Free;

end;


퀴즈 2.  [소스 2]는 Deadlock이 될까요, 아니면 잘 넘어 갈까요?






(1)번과 (2)번의 수행 차이점을 파악을 했다고 하면 다음과 같은 사실을 할 수가 있습니다. 상기는 TThread.WaitFor 코드입니다(pseudo code).


function TThread.WaitFor: LongWord;

  // ...

begin

  // ...

  if GetCurrentThreadID = MainThreadID then

  begin

    // 만약 WaitFor를 호출한 스레드가 메인 스레드라면, 메세지 처리를 하고 스레드의 종료를 기다린다.

    PeekMessage(Msg, 0, 0, 0, PM_NOREMOVE);

    MsgWaitForMultipleObjects(2, H, False, 1000, QS_SENDMESSAGE);

  end else

    // 만약 WaitFor를 호출한 스레드가 메인 스레드가 아니라면, 메세지 처리 같은 것 없이 그냥 스레드의 종료를 기다린다.

    WaitForSingleObject(H[0], INFINITE);

end;


여기에서 알 수가 있는 것은 SendMessage(혹은 Synchronize)에 의해서 메인 스레드와 User defined 스레드의 Deadlock을 막기 위해서 상기와 같이 WaitFor 내부에서 Window 메세지를 처리해 주는 것을 알 수가 있습니다.  델파이에서는 이러한 방식으로 메인 스레드와 기타 스레드간의 Deadlock을 막고 있습니다.






그런데, 만약 MyThread를 생성, 대기, 해제하는 코드가 메인 스레드(버튼 클릭 이벤트에서 코딩)가 아니고 또 다른 스레드에서 실행한면 어떻게 될까요?


[소스 3]

  TWorkThread = class(TThread)

    procedure Execute; override;

  end;


procedure TWorkThread.Execute;

var

  MyThread: TMyThread;

begin

  MyThread := TMyThread.Create(false);

  Sleep(2000);

  MyThread.WaitFor; // <--- (3)

  MyThread.Free;

end;


procedure TForm1.Button3Click(Sender: TObject);

var

  WorkThread: TWorkThread;

begin

  WorkThread := TWorkThread.Create(false);

  Sleep(2000);

  WorkThread.WaitFor; // <--- (4)

  WorkThread.Free;

end;


(3)번 코드를 보면 WaitFor를 호출하는 스레드는 메인 스레드가 아닌 WorkThread입니다. 이경우에는 (3)번에서는 PeekMessage를 하지 않겠죠.

퀴즈 3.  [소스 3]는 Deadlock이 될까요, 아니면 잘 넘어 갈까요?