我有这样的方法:
public async Task DoStuff(){
//stuff
using(var DisposableThing=DisposableThingProvider.GetThing()){
foreach(var A in AList){
foreach(var B in A){
if(!await B.GetBoolAsync()){
//Error Stuff
}
else{
//Different Stuff
}
}
}
}
}
如果B.GetBoolAsync()为false,则程序执行跳转到using语句的末尾。 B.GetBoolAsync()不会导致异常。 // Error Stuff在跳出两个foreach语句之前执行。 为什么会这样?
答案 0 :(得分:1)
这是因为您正在尝试访问正在迭代的变量而不等待该迭代返回。 A的值在A.GetBoolAsync返回之前发生变化。这会导致迭代器和返回值之间出现争用条件。
IEnumerable以这种方式不是线程安全的 - 请查看Jon Skeet的blog post如何处理这个问题。
单独注意,您应该以不同的方式构造代码,以便错误条件在else中,例如
if(await B.GetBoolAsync()){
//Different Stuff
}
else{
//Error Stuff
}
答案 1 :(得分:1)
演示正常工作 - 没有竞争条件。
class Program
{
static void Main()
{
var task = DoStuff();
Console.WriteLine( "Calling thread returned control to caller" );
Task.WaitAll( task );
Console.WriteLine( "Aync method call completed" );
Console.ReadLine();
}
public static async Task DoStuff()
{
var items = new List<TestA>()
{
new TestA() { Id = 10 },
new TestA() { Id = 20 },
new TestA() { Id = 30 },
};
foreach( var ta in items )
{
ta.Bs.Add( new TestB() { Id = 1 } );
ta.Bs.Add( new TestB() { Id = 2 } );
ta.Bs.Add( new TestB() { Id = 3 } );
}
using( var db = new System.Data.SqlClient.SqlConnection() )
{
Console.WriteLine( "Entered 'using'" );
foreach( var a in items )
{
foreach( var b in a.Bs )
{
if( !await b.GetBoolAsync() )
{
Console.WriteLine( "{0}: false", a.Id + b.Id );
}
else
{
Console.WriteLine( "{0}: true", a.Id + b.Id );
}
}
}
Console.WriteLine( "Leaving 'using'");
}
Console.WriteLine( "Left 'using'");
}
}
public class TestA
{
public int Id { get; set; }
public List<TestB> Bs { get; set; }
public TestA()
{
Bs = new List<TestB>();
}
}
public class TestB
{
public int Id { get; set; }
public async Task<bool> GetBoolAsync()
{
await Task.Delay(1);
return Convert.ToBoolean( this.Id % 2 );
}
}
输出: