스레드에서 직접 컨트롤을 접근하게 되면 크로스 스레드 에러가 발생합니다.
스레드에서 컨트롤을 접근하는 것은 컨트롤을 일관되지 않은 상태로 만들 수 있고, 심각한 오류나 프로그램 중단을 초래할 수 있습니다. 따라서 스레드에서 직접 컨트롤을 접근하는 것은 안전하지 않은 호출 방법입니다.
다음은 크로스 스레드 에러를 발생시키는 예제 코드입니다.
private void Button1_Click(object sender, EventArgs e)
{
thread = new Thread(SetName);
thread.Start();
}
private void SetName()
{
txtName.Text = "SSABI"; // 크로스 스레드 에러 발생!
}
크로스 스레드 에러를 해결하게 위해서는 System.Windows.Forms.Control.Invoke
또는 System.ComponentModel.BackgroundWorker
를 사용해야 합니다.
크로스 스레드 에러를 해결하는 2가지 방법을 설명드리겠습니다.
Invoke
스레드에서 델리게이트(Delegate)를 사용해서 메소드를 호출하는 방법입니다.
다음은 Invoke
를 사용하는 예제 코드입니다.
public class Form1 : Form1
{
private delegate void SetTextSafeDelegate(string text);
private Thread thread = null;
private void Button1_Click(object sender, EventArgs e)
{
thread = new Thread(SetText);
thread.Start();
}
private void SetTextSafe(string text)
{
if(textBox1.InvokeRequired)
{
SetTextSafeDelegate del = new SetTextSafeDelegate(SetTextSafe);
textbox1.Invoke(del, new object[]{text});
}
else
{
textbox1.Text = text;
}
}
private void SetText()
{
SetTextSafe("Use Invoke");
}
}
스레드에서 호출할 델리게이트를 정의하고 Invoke
를 사용하여 델리게이트를 호출합니다.InvokeRequired
는 다른 스레드로부터 호출되었을 경우 Invoke
를 사용 해야 하는지 여부를 알려줍니다.InvokeRequired
가 true
일 경우 Invoke
를 사용해서 델리게이트를 넘겨 주어야 하고,false
일 경우 직접 컨트롤을 접근할 수 있습니다.Invoke
메소드의 인자는 Invoke(delegate 델리게이트, object[] 파라미터)
로 넘겨줍니다.
BackgroundWorker
스레드에서 BackgroundWorker
클래스를 사용하는 방법입니다.BackgroundWorker
는 UI 스레드와는 별도로 백그라운드에서 비동기적 작업을 수행하는데 사용됩니다.
다음은 BackgroundWorker
를 사용하는 예제 코드입니다.
public partial class Form1 : Form
{
private BackgroundWorker backgroundWorker;
public Form1()
{
InitializeComponent();
backgroundWorker = new BackgroundWorker();
backgroundWorker.DoWork += new DoWorkEventHandler(BackgroundWorkerDoWork);
backgroundWorker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(BackgroundWorkerRunWorkerCompleted);
}
private void button1_Click(object sender, EventArgs e)
{
backgroundWorker.RunWorkerAsync();
}
private void BackgroundWorkerDoWork(object sender, DoWorkEventArgs e)
{
Thread.Sleep(1000);
e.Result = "Use BackgroundWorker";
}
private void BackgroundWorkerRunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
lblText.Text = e.Result.ToString();
}
}
BackgroundWorker
객체를 생성합니다.BackgroundWorker
의 DoWork
에 백그라운드 스레드에서 수행하는 메소드를 이벤트 핸들러에 등록하고,RunWorkerCompleted
에 DoWork
작업이 모두 끝났을 때 처리하는 메소드를 이벤트 핸들러에 등록합니다.DoWork
의 작업이 완료되면 Result에 결과를 대입해 주고 RunWorkerCompleted
에서 컨트롤에RunWorkerAsync()
를 호출하면 DoWork
가 실행됩니다.
댓글