2013-09-09 6 views
25

Я пытаюсь немного поиграть с асинхронным/ожиданием/продолжением C#. Моя цель состоит в том, чтобы иметь две задачи, которые работают параллельно, хотя какая задача выполняет последовательность действий по порядку. Для этого я планировал иметь List<Task>, которые представляют задачи 2 (или более), выполняющиеся параллельно, и использовать ContinueWith для каждого из Task . Моя проблема заключается в том, что обратный вызов в продолжении, похоже, не выполняется, пока await taskList уже вернулся.Использовать асинхронный обратный вызов с помощью Task.ContinueWith

Для того, чтобы подвести итоги, вот пример, чтобы проиллюстрировать то, что я ожидал случиться:

class Program 
{ 
    static public async Task Test() 
    { 
     System.Console.WriteLine("Enter Test"); 
     await Task.Delay(100); 
     System.Console.WriteLine("Leave Test"); 
    } 

    static void Main(string[] args) 
    { 
     Test().ContinueWith(
     async (task) => 
     { 
      System.Console.WriteLine("Enter callback"); 
      await Task.Delay(1000); 
      System.Console.WriteLine("Leave callback"); 
     }, 
     TaskContinuationOptions.AttachedToParent).Wait(); 
     Console.WriteLine("Done with test"); 
    } 
} 

Ожидаемый результат будет

Enter Test 
Leave Test 
Enter callback 
Leave callback 
Done with test 

Однако выход

Enter Test 
Leave Test 
Enter callback 
Done with test 

Есть ли способ сделать задачу, на которой вызван ContinueWith, дефокусировать функцию, прежде чем считаться выполненной? то есть. .Wait будет ждать обе задачи, которые будут завершены, оригинал, и тот, который возвращается ContinueWith

+1

Попробуйте удалить 'await' из' await Task.Delay (1000); ' –

+1

Действительно, это сработало. Я убрал ожидание, и асинхронно, так как ничего не ждет. Однако мне не хватает причины для его работы. У вас есть теоретическое объяснение? – chouquette

+0

Появляется сообщение «Leave callback» после «Done with test»? –

ответ

30

При объединении нескольких задач с помощью метода ContinueWith, ваш тип возвращаемого значения будет Task<T> тогда T является тип возвращаемого значения делегат/метод передан ContinueWith.

В качестве типа возвращаемого в асинхронном делегата является Task, вы будете в конечном итоге с Task<Task> и в конечном итоге ожидания для асинхронной делегата вернуть вам Task которое сделано после первого await.

Для исправления этой ситуации вам необходимо использовать возвращенный Task, встроенный в ваш Task<Task>. Используйте метод расширения Unwrap, чтобы извлечь его.

+0

Это работало как шарм! Спасибо! – chouquette

4

Я хотел бы добавить свой ответ, чтобы дополнить уже принятый ответ. В зависимости от того, что вы пытаетесь сделать, часто можно избежать дополнительной сложности делегатов async и завершенных задач. Например, код может быть повторно учитываться как это:

class Program 
{ 
    static async Task Test1() 
    { 
     System.Console.WriteLine("Enter Test"); 
     await Task.Delay(100); 
     System.Console.WriteLine("Leave Test"); 
    } 

    static async Task Test2() 
    { 
     System.Console.WriteLine("Enter callback"); 
     await Task.Delay(1000); 
     System.Console.WriteLine("Leave callback"); 
    } 

    static async Task Test() 
    { 
     await Test1(); // could do .ConfigureAwait(false) if continuation context doesn't matter 
     await Test2(); 
    } 

    static void Main(string[] args) 
    { 
     Test().Wait(); 
     Console.WriteLine("Done with test"); 
    } 
} 
17

Когда вы делаете async программирования, вы должны стремиться заменить ContinueWith с await, как таковой:

class Program 
{ 
    static public async Task Test() 
    { 
    System.Console.WriteLine("Enter Test"); 
    await Task.Delay(100); 
    System.Console.WriteLine("Leave Test"); 
    } 

    static async Task MainAsync() 
    { 
    await Test(); 
    System.Console.WriteLine("Enter callback"); 
    await Task.Delay(1000); 
    System.Console.WriteLine("Leave callback"); 
    } 

    static void Main(string[] args) 
    { 
    MainAsync().Wait(); 
    Console.WriteLine("Done with test"); 
    } 
} 

код с помощью await намного чище и проще в обслуживании.

Кроме того, вы не должны использовать родительские/дочерние задачи с задачами async (например, AttachedToParent). Они не были предназначены для совместной работы.