早期使用块结束

时间:2014-02-03 23:26:56

标签: c# asynchronous task

我有这样的方法:

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语句之前执行。 为什么会这样?

2 个答案:

答案 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 );
    }
}

输出:

enter image description here