[c#] 멀티 뜨레드에서 lock과 await 같이 쓰기
아래의 코드에서 TestMethod1는 멀티 뜨레드에서 작동한다. 이 메떠드는 다른 뜨레드에서 실행되는 메떠드와 동기화되어야 한다. 그러면서 await도 해야 한다.
await를 lock 블록 안에서 쓰면 오류로 처리된다. 만약 await가 긴 시간을 쓴다면 lock이 걸린 다른 뜨레드는 그 시간을 기다려야 하기 때문이다. 이러면 멀티 뜨레드를 쓰는 의미가 줄어든다.
아래의 코드처럼 lock 블록 안에서 네스티드 메떠드를 만들어 await를 쓰면 오류로 처리되지는 않는다. 하지만 await부터 lock은 풀린다. 따라서 TestClass1.Int = 1과 TestClass1.Int = 2는 동기화되지 않아 race가 일어날 수 있다.
class TestClass
{
public int Int;
}
TestClass TestClass1 = new();
object LockObject1 = new();
private void button1_Click(object sender, EventArgs e)
{
Task.Run(TestMethod1);
}
void TestMethod1()
{
lock (LockObject1)
{
// ...
Task.Run(method);
async Task method()
{
await Task.Delay(5000);
TestClass1.Int = 1;
}
}
}
private void button2_Click(object sender, EventArgs e)
{
lock (LockObject1)
{
// ...
TestClass1.Int = 2;
}
Text = TestClass1.Int.ToString();
}
await는 이것대로 작동하게 하고 그 뒤부터 다시 동기화를 하려면 아래와 같이 다른 lock 오브젝트를 이용하여 다시 록을 걸어야 한다.
object LockObject1 = new();
object LockObject2 = new();
private void button2_Click(object sender, EventArgs e)
{
lock (LockObject1)
{
// ...
lock (LockObject2)
{
TestClass1.Int = 2;
}
}
Text = TestClass1.Int.ToString();
}
void TestMethod1()
{
lock (LockObject1)
{
// ...
Task.Run(method);
async Task method()
{
await Task.Delay(5000);
lock (LockObject2)
{
TestClass1.Int = 1;
}
}
}
}