델파이 어플리케이션은 기본적으로 "싱글 쓰레드" 로 동작한다.
그래서 개발한 프로그램이 시간이 오래걸리는 동작을 할 때, 사용자가 마우스로 화면을 클릭을 하는 등의 다른
이벤트를 발생시키면 프로그램이 응답없음 상태가 된다.
위와 같은 문제를 해결하기 위해 "Application.Processmessages;" 프로시져를 사용하곤 하는데,
이 프로시져의 특성 때문에 오히려 프로그램이 오작동하는 경우가 있다는 내용의 글을 보고 정리를 해보았다.
참고 URL :
- 볼랜드포럼 : http://www.borlandforum.com/impboard/impboard.dll?action=read&db=bcb_tip&no=1099
- Delphi.about.com [The dark side of Application.Processmessages in Delphi application] :
https://www.thoughtco.com/dark-side-of-application-processmessages-1058203
(위 볼랜드포럼 게시글 내용 안에 참고하라고 링크 달아놓은 글이다.)
Application.Processmessages; 는 Button click event 혹은 Window Movement 와 같은 모든 대기 메시지를
핸들링(처리) 해주는 Procedure 이다.
예시로, DB에 데이터를 만 건 이상 Insert 하는 버튼을 만들어서 클릭하면, 데이터가 insert 되는 시간동안
(그 버튼의 Click Event가 완료될 때 까지) 싱글 쓰레드인 델파이 어플리케이션은 Lock 상태가 된다.
procedure TForm1.Button1Click(Sender: TObject);
var
i :Integer;
begin
for i := 0 to 10000 do
begin
{Insert Into ....}
end;
{for문이 끝날때까지 Application은 Lock 상태가 된다.}
end;
이 때, 매 반복마다 (혹은 다음단계로 넘어갈 때 마다) Application.Processmessages; 프로시져를 호출해주면 버튼의
Click Event가 처리되면서 대기열에 또 다른 이벤트를 받을 수 있는 상태가 된다.
procedure TForm1.Button1Click(Sender: TObject);
var
i :Integer;
begin
for i := 0 to 10000 do
begin
{Insert Into ....}
Application.ProcessMessages; //추가
end;
{for문이 끝날때까지 Application은 Lock 상태가 된다.}
end;
(반복문이 동작하는 도중에도 어플리케이션을 드래그해서 위치를 옮기는 등의 행위를 할 수 있다.)
따라서 내가 만든 프로그램이 "응답없음" 상태가 되는것을 방지할 수 있게 되는것인데,
이런식의 코딩을 할 때는 반드시 주의해야 할 점이 있다.
Application.Processmessages; 프로시져로 인해 여러 이벤트들의 Sync가 꼬일 수 있게 되는것이 문제점인데,
사용자가 같은 버튼을 두 번 이상 빠르게 클릭하면 마치 버튼이 재귀호출 되는 것처럼 작동할 수 있게 된다.
{ In MyForm: }
WorkLevel :Integer;
{ Oncreate: }
WorkLevel := 0;
procedure TForm1.ButtonWorkClick(Sender: TObject);
var
cycle : integer;
begin
inc(WorkLevel);
for cycle := 1 to 5 do
begin
Memo1.Lines.Add('- Work ' + IntToStr(WorkLevel) + ', Cycle ' + IntToStr(cycle) );
Application.ProcessMessages;
sleep(1000) ; // or some other work
end;
Memo1.Lines.Add('Work ' + IntToStr(WorkLevel) + ' ended.');
dec(WorkLevel);
end;
(소스 출처 : 맨 위 링크 중 Delphi.about.com 의 글 중에서)
위와 같이 프로그램을 작성하고 버튼을 두번 클릭한다고 가정했을 때, Memo1에 아래와 같이 출력되기를 기대할 수 있다.
하지만, 사용자가 첫번째 클릭이벤트가 종료되기 전에 두번째로 버튼을 클릭하면 아래처럼 결과가 나타난다.
Application.Processmessages; 프로시져에 의해서 첫번째 버튼 클릭 이벤트로 인한 Lock이 해제되면서, 이벤트 진행 도중 또다시 클릭이벤트가 발생할 수 있게된 상황이다.
(쓰레드의 sync가 맞지않아 마치 버튼 클릭 이벤트가 재귀호출되는 것 처럼 보인다.)
만약 파일생성이나 메모리 접근 등의 작업을 처리하는 버튼이 위처럼 호출된다면 Access Violation 에러가 발생할 수도 있는 상황일 것이다.
사실 위와 같은 상황은,
클릭 이벤트 맨 처음에 ButtonWork.Enabled := False; 를 쓰고, 이벤트 맨 마지막에 다시 True로 활성화 시켜주면 간단하게 회피할 수 있기 때문에..
개발자가 이런 상황을 고려하지 않고 무작정 Processmessages; 를 사용하지 않도록 주의만 잘 하고 사용하면 훨씬 사용자 친화적인 프로그램을 만들 수 있을것 같다는게 내 생각이다.
'Delphi' 카테고리의 다른 글
[델파이/Delphi] 열거형(Enum) 사용 예시 (0) | 2021.11.01 |
---|---|
[델파이/Delphi] "Interface not supported" 에러 (ExcelExport) (0) | 2021.10.26 |
[Delphi/델파이] 최근 에러 조회하기 - GetLastError (0) | 2021.10.15 |
[Delphi/델파이] Theme Editor (테마 에디터) 소개 (0) | 2021.07.04 |
[Delphi/델파이] - Free vs FreeAndNil (0) | 2020.06.27 |