在C#中正确使用取消令牌

问题描述 投票:2回答:1

[我最近接触过C#语言,正在尝试从cassandra中获取数据,所以我正在使用下面的代码从cassandra中获取数据,并且工作正常。

[我唯一的问题是我的ProcessCassQuery方法中-我正在将CancellationToken.None传递给我的requestExecuter函数,这可能不是正确的选择。处理该案件的正确方法应该是什么,正确处理该怎么办?

/**
 *
 * Below method does multiple async calls on each table for their corresponding id's by limiting it down using Semaphore.
 *
 */
private async Task<List<T>> ProcessCassQueries<T>(IList<int> ids, Func<CancellationToken, int, Task<T>> mapperFunc, string msg) where T : class
{
    var tasks = ids.Select(async id => 
    {
        await semaphore.WaitAsync();
        try
        {
            ProcessCassQuery(ct => mapperFunc(ct, id), msg);
        }
        finally
        {
            semaphore.Release();
        }
    });

  return (await Task.WhenAll(tasks)).Where(e => e != null).ToList();
}

// this might not be good idea to do it. how can I improve below method?
private Task<T> ProcessCassQuery<T>(Func<CancellationToken, Task<T>> requestExecuter, string msg) where T : class
{
    return requestExecuter(CancellationToken.None);
}
c#
1个回答
2
投票

[如official documentation中所述,取消令牌允许传播取消信号。例如,这对取消长期运行的操作很有用,这些操作由于某种原因不再有意义,或者只是花费太长时间。

CancelationTokenSource将允许您获取可传递给requestExecutor的自定义令牌。它还将提供取消正在运行的Task的方法。

private CancellationTokenSource cts = new CancellationTokenSource();

// ...

private Task<T> ProcessCassQuery<T>(Func<CancellationToken, Task<T>> requestExecuter, string msg) where T : class
{
    return requestExecuter(cts.Token);
}

示例

让我们看一个不同的最小/虚拟示例,以便我们看一下它的内部。

考虑以下方法,GetSomethingAsync将产生每秒返回一个递增的整数。

如果通过外部动作取消了此过程,则对token.ThrowIfCancellationRequested的调用将确保引发TaskCanceledException。可以采用其他方法,例如,检查token.IsCancellationRequested是否为真并对其进行处理。

private static async IAsyncEnumerable<int> GetSomethingAsync(CancellationToken token)
{
    Console.WriteLine("starting to get something");

    token.ThrowIfCancellationRequested();

    for (var i = 0; i < 100; i++)
    {
        await Task.Delay(1000, token);
        yield return i;
    }

    Console.WriteLine("finished getting something");
}

现在让我们构建main方法来调用上述方法。

public static async Task Main()
{    
    var cts = new CancellationTokenSource();

    // cancel it after 3 seconds, just for demo purposes
    cts.CancelAfter(3000);
    // or: Task.Delay(3000).ContinueWith(_ => { cts.Cancel(); });

    await foreach (var i in GetSomethingAsync(cts.Token))
    {
        Console.WriteLine(i);
    }
}

如果我们运行此命令,我们将获得如下输出:

starting to get something
0
1
Unhandled exception. System.Threading.Tasks.TaskCanceledException: A task was canceled.

当然,这只是一个虚拟的例子,取消可以由用户操作或发生的某些事件触发,它不一定是计时器。

© www.soinside.com 2019 - 2024. All rights reserved.