Подтвердить что ты не робот

Связывание маркеров отмены

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

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

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

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

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

4b9b3361

Ответ 1

Вы хотите использовать 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: False

Отменить родительский CTS
Ребенок CTS: истинный
Родительский CTS: True

Ответ 2

Как i3arnon уже ответил, вы можете сделать это с помощью 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. Надеюсь, это невозможно, но если это было возможно, это будет считаться плохой практикой и вообще не о чем беспокоиться.

Ответ 3

Если у вас есть только CancellationToken, а не CancellationTokenSource, тогда все еще можно создать связанный токен отмены. Вы бы просто использовали метод Register чтобы вызвать отмену (псевдо) дочернего элемента:

var child = new CancellationTokenSource();
token.Register(child.Cancel);

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

child.CancelAfter(cancelTime);
token = child.Token;