2015-02-17 3 views
6

Я пытаюсь понять поведение задач .net, когда привязаны дети.Детское задание отменено, родитель завершен?

Я следующий код теста:

void Test() 
{ 
    var tokenSource = new CancellationTokenSource(); 
    var token = tokenSource.Token; 

    Task child = null; 
    var parent = Task.Factory.StartNew(() => 
    { 
     child = Task.Factory.StartNew(() => 
     { 
      while (!token.IsCancellationRequested) 
       Thread.Sleep(100); 
      token.ThrowIfCancellationRequested(); 
     }, token, TaskCreationOptions.AttachedToParent, TaskScheduler.Default); 
    }, token); 

    Thread.Sleep(500); 

    Debug.WriteLine("State of parent before cancel is {0}", parent.Status); 
    Debug.WriteLine("State of child before cancel is {0}", child.Status); 

    tokenSource.Cancel(); 
    Thread.Sleep(500); 

    Debug.WriteLine("State of parent is {0}", parent.Status); 
    Debug.WriteLine("State of child is {0}", child.Status); 
} 

Результат этого:

State of parent before cancel is WaitingForChildrenToComplete 
State of child before cancel is Running 
A first chance exception of type 'System.OperationCanceledException' occurred in mscorlib.dll 
State of parent is RanToCompletion 
State of child is Canceled 

Aparrently государственная задача родителя не Canceled, даже если обе задачи разделяют одну фишку, и ребенок прилагается.

Как вернуть родительскую задачу в состояние возврата Canceled при аннулировании?

ПРИМЕЧАНИЕ Если я выдаю исключение, то обе задачи возвращают Faulted.

+1

Это 'Faulted', это особое состояние здесь, где (по какой-либо причине) состояние childs отражается в родительском. «Отказано» специально [вызывается] (http://blogs.msdn.com/b/pfxteam/archive/2009/08/30/9889070.aspx) как «Чтобы закончить в состоянии« Отменено », задача должна либо иметь аннулирование, запрашиваемое до начала выполнения, или оно должно подтвердить запрос на отмену во время его исполнения ». и ни одно из них не относится к родителям. –

+0

Таким образом, родительская задача должна ждать дочерней задачи и бросать ее собственное 'OperationCanceledException' для ее состояния, которое нужно отменить? – Anders

ответ

4

Ожидаемое поведение, указанное в MSDN. Родительская задача имеет значение wait (прокрутите вниз до раздела отмены) для дочерней задачи. Родительская задача должна обрабатывать все доброкачественные ошибки (например, аннулирование).

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

Task child = null; 
var parent = Task.Factory.StartNew(() => 
{ 
    child = Task.Factory.StartNew(() => 
    { 
    while (!token.IsCancellationRequested) Thread.Sleep(100); 
    token.ThrowIfCancellationRequested(); 
    }, token, TaskCreationOptions.AttachedToParent, TaskScheduler.Default); 

    // This is the magic line. 
    child.Wait(token); 
}, token); 

Если вы используете этот код, чтобы сделать что-то продуктивное, а не только для тестирования, вы должны также рассмотреть возможность использования упрощенной Task.Run(), который поддерживает async делегатов вместо Task.Factory.StartNew(). Это article очень интересно.

+0

Где документировано, что это ожидаемое поведение? Я не могу найти его. –

+0

См. Ссылку на MSDN, добавленную мной в моем ответе. – Krumelur

+0

Я прочитал эту ссылку, Не могу найти ее. В нем говорится, что вам нужно подождать, пока родительская задача будет обрабатывать исключение, а не дочернюю задачу, как вы говорите в своем ответе. Можете ли вы привести точный текст, что означает это? –

1

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

Давайте из рабочего примера:

void Test() 
{ 
    var tokenSource = new CancellationTokenSource(); 
    var token = tokenSource.Token; 

    Task child = null; 
    var parent = Task.Factory.StartNew(() => 
    { 
     child = Task.Factory.StartNew(() => 
     { 
      while (!token.IsCancellationRequested) 
       Thread.Sleep(100); 
      token.ThrowIfCancellationRequested(); 
     }, token, TaskCreationOptions.AttachedToParent, TaskScheduler.Default); 

     while (!token.IsCancellationRequested) 
      Thread.Sleep(100); 
     token.ThrowIfCancellationRequested(); 
    }, token); 

    Thread.Sleep(500); 

    Debug.WriteLine("State of parent before cancel is {0}", parent.Status); 
    Debug.WriteLine("State of child before cancel is {0}", child.Status); 

    tokenSource.Cancel(); 
    Thread.Sleep(500); 

    Debug.WriteLine("State of parent is {0}", parent.Status); 
    Debug.WriteLine("State of child is {0}", child.Status); 
} 

Для того родителя должны быть отменены, вам нужно позвонить куда-нибудь в теле родителя token.ThrowIfCancellationRequested(). Однако просто позвонить token.ThrowIfCancellationRequested() будет недостаточно.

Вам необходимо концептуализировать, как parent и child потоки выполнения идут вместе, чтобы понять, почему ваши ожидания неверны.

Main thread: ---\------------------------------------[Cancel]-----/ 
Parent:   \---\-----[Check cancellation]------------------/ 
Child:    \------------------------------[Cancel]---/ 

Как вы можете видеть из приведенной выше диаграммы, родитель проверяет способ отмены до запроса на отмену. Ребенок получает сигнал отмены, потому что он в основном ждет, когда будет отменена аннулирование. Теперь, если вы поместите тот же механизм в родительском, он получит сигнал отмены, потому что он не завершил свою работу до того, как будет подана аннулирование.

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