2009년 04월 16일
[C#] 다중스레드이용시 안전한 방식으로 Windows Forms 컨트롤 호출하기.
다중스레드의 윈도우폼 프로그램을 제작할시 해당 프로그램의 폼에 위치한 컨트롤을 조작하는 스레드가 두개 이상이 있을경우 컨트롤이 일관성없는 상태가 될수 있습니다. 즉 결합상태, 교착상태 등의 다른 스레드 버그가 발생할수 있습니다. 이를 막기 위해서는 컨트롤에 대한 엑세스가 스레드로부터 안전한 방식으로 수행되는지를 검사하여야합니다.
한가지 예를 들어보겠습니다.
가정 : 윈도우폼 프로그램으로 해당 폼에 txt_Text 라는 TextBox 컨트롤이 있다고 가정하겠습니다.
using System;
using System.ComponentModel;
using System.Windows.Forms;
using System.Thread;
namespace CrossThread
{
public class Form1 : Form
{
TextBox txt_Text;
Thread th;
int count;
/* Constructor */
public Form1()
{
InitializeComponent();
count = 0;
th = new Thread( new ThreadStart( Run ) );
th.Start();
}
/* Thread Method */
public void Run()
{
txt_Text.Text = Convert.ToString( count );
count++;
Thread.Sleep(1000);
}
protected void InitializeComponent()
{
/* 디자이너 코드 생략 */
}
[STAThread]
public static void main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new Form1());
}
}
위의 예에서는 추가한 th 스레드가 돌면서 TextBox에 count 변수에 있는 값을 1초간격으로 집어넣게 했습니다.
보시기에는 아무런 문제가 없어 보이지만 Runtime시에 Thread Method 인 Run 메소드의
txt_Text.Text = Convert.ToString(count);
에서 InvalidOperationException 가 발생하며
"txt_Text 컨트롤이 자신이 만들어진 스레드가 아닌 다른스레드에서 엑세스 되었습니다."
라는 메세지를 표시합니다.
이를 피하고 해당 컨트롤이 만들어진 스레드가 아닌 다른 스레드에서 해당 컨트롤을 호출하려면 대리자를 이용하여 Invoke를 호출합니다.
해당 방법의 순서는
1. 해당 컨트롤의 InvokeRequired 속성을 확인합니다.
2. 해당 컨트롤의 InvokeRequired 가 true를 반환한다면 대리자를 이용하여 Invoke를 호출합니다.
3. false를 반환할경우에는 컨트롤을 직접호출합니다.
using System;
using System.ComponentModel;
using System.Windows.Forms;
using System.Thread;
namespace CrossThread
{
public class Form1 : Form
{
TextBox txt_Text;
Thread th;
int count;
delegate void SetTextCallBack( string text );
/* Constructor */
public Form1()
{
InitializeComponent();
count = 0;
th = new Thread( new ThreadStart( Run ) );
th.Start();
}
private void SetText( string text )
{
if (this.txt_Text.InvokeRequired)
{
SetTextCallBack dele = new SetTextCallBack(SetText);
this.Invoke( dele, new object[] { text } );
}
else
{
this.txt_Text.Text = text;
}
}
/* Thread Method */
public void Run()
{
SetText( Convert.ToString( count ) );
count++;
Thread.Sleep(1000);
}
protected void InitializeComponent()
{
/* 디자이너 코드 생략 */
}
[STAThread]
public static void main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new Form1());
}
}
# by | 2009/04/16 14:53 | 프로그래밍이야기 | 트랙백 | 덧글(5)






☞ 내 이글루에 이 글과 관련된 글 쓰기 (트랙백 보내기) [도움말]
before after 로 잘 정리 해 놓으셨네요.