2014-02-03 3 views
0

У меня есть метод, как это:Использование блока Окончание Early

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() ложно, то выполнение программы переходит к концу с помощью заявления. B.GetBoolAsync() не вызывает исключения. // Error Stuff Выполняется перед тем, как выпрыгнуть из обоих команд foreach. Почему это происходит?

+0

Вы выполнения нескольких 'DoStuff' одновременно (или рекурсивно из' GetBoolAsync')? – Noseratio

+0

показать код, который вызывает 'DoStuff()' – Moho

+1

Вам нужно показать свой реальный код или, по крайней мере, простой рабочий набор кода, который * воспроизводит * вашу проблему. Код, который вы показываете, не является проблемой. –

ответ

1

Это потому, что вы пытаетесь получить доступ к переменной, которую вы итерации продолжаете, не дожидаясь возвращения этой итерации. Значение A изменяется до возвращения A.GetBoolAsync. Это вызывает состояние гонки между итератором и возвращаемым значением.

IEnumerable не являются потокобезопасными таким образом - ознакомьтесь с blog post от Jon Skeet, как справиться с этим.

На отдельной ноте, вы, вероятно, следует структурировать свой код по-разному, так что условие ошибки в другой, как

if(await B.GetBoolAsync()){ 
    //Different Stuff 
} 
else{ 
    //Error Stuff 
} 
+0

выполнение потока должно прекратиться до тех пор, пока операция async не завершится из-за ожидания, так как B получает новое значение, когда блок итерации содержит оператор ожидания? – Moho

+0

Из [Документов MSDN] (http://msdn.microsoft.com/en-us/library/hh156528.aspx): выражение ожидания не блокирует поток, на котором он выполняется. Вместо этого он заставляет компилятор подписывать остальную часть асинхронного метода как продолжение на ожидаемой задаче. Затем элемент управления возвращается к вызывающей стороне метода async. Когда задача завершается, она вызывает свое продолжение, и выполнение метода async возобновляется там, где оно было остановлено. – Codeman

+0

правильно, но выполнение самого метода остановлено. – Moho

1

Demo'd работает должным образом - нет состояния гонки.

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

Смежные вопросы