2015-04-14 4 views
14

Я использую маркер отмены, который передается, так что мое обслуживание может быть отключено чисто. У службы есть логика, которая пытается подключиться к другим службам, поэтому токен - хороший способ вырваться из этих циклов повтора, запущенных в отдельных потоках. Моя проблема в том, что мне нужно сделать вызов службе, которая имеет внутреннюю логику повтора, но вернуться после заданного периода, если попытка повтора не выполнена. Я хотел бы создать новый токен отмены с тайм-аутом, который сделает это для меня. Проблема заключается в том, что мой новый токен не связан с маркером «master», поэтому, когда главный токен отменен, мой новый токен будет по-прежнему оставаться в живых до тех пор, пока не истечет время ожидания или не будет создано соединение, и оно будет возвращено. То, что я хотел бы сделать, - это связать два токена вместе, так что, когда главный будет отменен, новый будет отменен. Я попытался использовать метод CancellationTokenSource.CreateLinkedTokenSource, но когда мой новый токен тайм-аута, он также отменил мастер-токен. Есть ли способ сделать то, что мне нужно делать с жетонами или это потребует внесения изменений в логику повторных попыток (вероятно, не будет в состоянии сделать это легко)Ссылка на токены отмены

Вот что я хочу сделать:

Мастер-токен - передал различные функции, чтобы служба могла отключиться. Временный токен - передается на одну функцию и устанавливается на таймаут через одну минуту

Если главный токен отменен, временный токен также необходимо отменить.

Срок действия временного токена не должен отменять мастер-токен.

ответ

17

Вы хотите использовать CancellationTokenSource.CreateLinkedTokenSource. Это позволяет иметь «родителя» и «ребенка» CancellationTokenSource es. Вот простой пример:

var parentCts = new CancellationTokenSource(); 
var childCts = CancellationTokenSource.CreateLinkedTokenSource(parentCts.Token); 

childCts.CancelAfter(1000); 
Console.WriteLine("Cancel child CTS"); 
Thread.Sleep(2000); 
Console.WriteLine("Child CTS: {0}", childCts.IsCancellationRequested); 
Console.WriteLine("Parent CTS: {0}", parentCts.IsCancellationRequested); 
Console.WriteLine(); 

parentCts.Cancel(); 
Console.WriteLine("Cancel parent CTS"); 
Console.WriteLine("Child CTS: {0}", childCts.IsCancellationRequested); 
Console.WriteLine("Parent CTS: {0}", parentCts.IsCancellationRequested); 

Выход, как и ожидалось:

Отменить ребенок CTS
Ребенок CTS: Правда
Родитель CTS: Ложные

Отменить родительское CTS
ребенка CTS: Правда
Родитель CTS: True

+2

Вы совершенно верны. Я начал использовать CancellationTokenSource.CreateLinkedTokenSource, но думал, что он не работает. Я забыл, что, когда токен разыгрывает, он выдает исключение. Это было уловлено в моем коде. Это создавало впечатление, что оно не работает так, как я ожидал. Положив мой вызов в блок catch try, он работал нормально. – Retrocoder

+0

@Retrocoder Если вы хотите только ловушку внутреннего токена, я бы рекомендовал использовать шаблон, например 'try {doSomething (ct: childCts.Token); } catch (OperationCancelledException), когда (childCts.IsCancellationRequested) {} ​​'. Вы можете поместить это внутри цикла повтора и создать источник дочернего токена внутри цикла. Затем, когда родительский токен отменяет, он будет пузыриться полностью, но когда дочерний токен отменяет, он просто выполняет повторную попытку. Я не могу сказать из вашего комментария - возможно, вы уже делаете это правильно ;-). – binki

0

Как i3arnon already answered, вы можете сделать это с помощью CancellationTokenSource.CreateLinkedTokenSource(). Я хочу попытаться показать, как использовать такой токен, когда вы хотите отличить отмену общей задачи от отмены дочерней задачи без отмены общей задачи.

async Task MyAsyncTask(
    CancellationToken ct) 
{ 
    // Keep retrying until the master process is cancelled. 
    while (true) 
    { 
     // Ensure we cancel ourselves if the parent is cancelled. 
     ct.ThrowIfCancellationRequested(); 

     var childCts = CancellationTokenSource.CreateLinkedTokenSource(ct); 
     // Set a timeout because sometimes stuff gets stuck. 
     childCts.CancelAfter(TimeSpan.FromSeconds(32)); 
     try 
     { 
      await DoSomethingAsync(childCts.Token); 
     } 
     // If our attempt timed out, catch so that our retry loop continues. 
     // Note: because the token is linked, the parent token may have been 
     // cancelled. We check this at the beginning of the while loop. 
     catch (OperationCancelledException) when (childCts.IsCancellationRequested) 
     { 
     } 
    } 
} 

Когда Временное Токен истекает он не должен отменить мастер токен.

Обратите внимание, что MyAsyncTask() «s подпись принимает CancellationToken, а не CancellationTokenSource. Поскольку метод имеет доступ только к членам на CancellationToken, он не может случайно отменить главный/родительский токен. Я рекомендую вам организовать ваш код таким образом, чтобы CancellationTokenSource из главной задачи отображался как можно меньше кода. В большинстве случаев это можно сделать, передав CancellationTokenSource.Token методам вместо обмена ссылкой на CancellationTokenSource.

Я не исследовал, но может быть способ с чем-то вроде отражения, чтобы принудительно отменить CancellationToken без доступа к его CancellationTokenSource. Надеюсь, это невозможно, но если это было возможно, это будет считаться плохой практикой и вообще не о чем беспокоиться.