C# 任务<;T>;。结果和字符串连接
我在玩C# 任务<;T>;。结果和字符串连接,c#,.net,c#-5.0,C#,.net,C# 5.0,我在玩async/await时遇到以下问题: class C { private static string str; private static async Task<int> FooAsync() { str += "2"; await Task.Delay(100); str += "4"; return 5; } private static void Main(st
async/await
时遇到以下问题:
class C
{
private static string str;
private static async Task<int> FooAsync()
{
str += "2";
await Task.Delay(100);
str += "4";
return 5;
}
private static void Main(string[] args)
{
str = "1";
var t = FooAsync();
str += "3";
str += t.Result; // Line X
Console.WriteLine(str);
}
}
然后是预期的“12345”结果
为什么会这样?(使用VS2012)
为什么会这样?(使用VS2012)
您正在控制台应用程序中运行此操作,这意味着没有当前的同步上下文
因此,wait
之后的fooancy()
方法部分在单独的线程中运行。当您执行str+=t.Result
时,实际上是在+=4
调用和+=t.Result
之间创建一个竞争条件。这是因为string+=
不是原子操作
如果要在Windows窗体或WPF应用程序中运行相同的代码,则同步上下文将被捕获并用于+=“4”
,这意味着这将在同一线程上运行,并且您不会看到此问题代码>扩展为x=x+y代码>在编译时
str+=t.结果代码>变为str=str+t.结果
,其中在获取t.Result
之前读取str
。此时,str
是“123”
。当fooanc
中的continuation运行时,它修改str
,然后返回5
。所以str
现在是“1234”
。但是,在fooancy
ran(即“123”
)中的continuation之前读取的str
值与5
相连,以分配给str
值“1235”
当你把它分成两条语句时,inti=t.Result;str+=i代码>,此行为不可能发生。这是竞争条件。您没有在两个执行线程之间正确同步对共享变量的访问
您的代码可能正在执行以下操作:
- 将字符串设置为“1”
- 调用异步
- 附加2
- 当调用wait时,main方法继续执行,foosync中的回调将在线程池中运行;从现在开始,事情是不确定的
- 主线程在字符串后面追加3
然后我们进入有趣的一行:
str += t.Result;
在这里,它被分解成几个较小的操作。它将首先获取当前值str
。此时,异步方法(很可能)还没有完成,所以它将是“123”
。然后它等待任务完成(因为Result
强制阻塞等待),并将任务的结果(在本例中为5
)添加到字符串末尾
在主线程已经获取当前值str
之后,异步回调将获取并重新设置str
,然后它将覆盖str
,而不会被读取,因为主线程很快就会覆盖它。如果return 5
发生在str+=4
之后的fooancy
?@IlyaKogan,因为+=/code>不是一个原子操作,怎么会有竞态条件呢。它被分解成几个子操作,这些子操作可以(在本例中是)相互交织以生成未定义的结果。@Ilykogan竞态条件之所以出现,是因为str+=t.Result
实际上是str=str+t.Result
,而“str”是在+=4
完成之前获取的,但还应该注意的是,您不应该这样做-使用非原子操作(无锁或其他线程逻辑)共享对变量的访问肯定会使您陷入不确定的情况。当分解为两个语句时,这种行为仍然可能发生。只是可能性不大。见里德的答案。
str += t.Result;