[c#] Contol.Invoke와 Control.BeginInvoke의 비교
Invoke와 BeginInvoke는 멀티 뜨레드를 다룰 때 자주 쓴다.
Invoke는 Control로 제어권이 넘어간 뒤 돌아올 때를 기다리고 BeginInvoke는 기다리지 않는다. 조금 더 어렵게 설명하면 전자는 synchronous 즉 동기적이고 후자는 asynchronous 즉 비동기적이다.
아래 예제에서 BeginInvoke를 한 거는 label 출력 명령을 떠나 보낸 뒤 바로 제 갈 길을 간다. 따라서 BeginInvoke가 실행된 뒤 그 끝을 기다리지 않고 바로 MessageBox.Show가 실행된다.
private void button1_Click(object sender, EventArgs e)
{
Task.Run(Method1);
}
void Method1()
{
label1.Invoke(invoke);
void invoke()
{
label1.Text = "1";
Thread.Sleep(2000);
}
MessageBox.Show("waited");
}
private void button2_Click(object sender, EventArgs e)
{
Task.Run(Method2);
}
void Method2()
{
label1.BeginInvoke(invoke);
void invoke()
{
label1.Text = "2";
Thread.Sleep(2000);
}
MessageBox.Show("immediate");
}
아래와 같은 경우 문제가 된다.
private void button1_Click(object sender, EventArgs e)
{
Task.Run(() => Method1());
}
void Method1()
{
for (int i = 0; i < 5; i++)
{
button1.Invoke(method);
void method()
{
button1.Text = i.ToString(); // 4
}
}
}
private void button2_Click(object sender, EventArgs e)
{
Task.Run(() => Method2());
}
void Method2()
{
for (int i = 0; i < 5; i++)
{
button2.BeginInvoke(method);
void method()
{
button2.Text = i.ToString(); // 5
}
}
}
보통의 경우라면 위의 for 구문에서 i는 4로 끝날 것으로 의도한다. 하지만 BeginInvoke를 쓰면 5로 끝난다. BeginInvoke가 실행되는 동안 다른 뜨레드에서 for가 i++를 실행하기 때문이다. 이런 문제를 피하려면 BeginInvoke 아닌 Invoke를 쓰는 게 좋다.