等待具有超时的单个事件

Waiting for single event with timeout

提问人:Daniel 提问时间:1/19/2023 更新时间:1/19/2023 访问量:147

问:

我当前的解决方案需要一个全局 CancellationTokenSource 类。没有它也可以做同样的事情吗?

CancellationTokenSource _waitCTS = null;

private async Task WaitWithTimeout(string param)
{
    _waitCTS?.Dispose();
    _waitCTS = new CancellationTokenSource();

    XClass x = new XClass(param);
    x.OnReceived += X_OnReceived;

    try
    {
        await Task.Delay(5000, _waitCTS.Token).ConfigureAwait(true);
    }
    catch (TaskCanceledException)
    {
        Debug.WriteLine("Timed out");
    }

    x.OnReceived -= X_OnReceived;
    x.Cleanup();
}

private void X_OnReceived(object sender, SelectedItemEventArgs e)
{
    Debug.WriteLine(e.ReceivedItem);
    if (_waitCTS != null)
        _waitCTS.Cancel();
}
C# .NET 异步 事件 CancellationTokenSource

评论


答:

2赞 David Browne - Microsoft 1/19/2023 #1

可以使处理程序成为捕获 CancellationTokenSource 变量的本地处理程序。例如:

private async Task WaitWithTimeout(string param)
{
    var CancellationTokenSource _waitCTS = new CancellationTokenSource();

    XClass x = new XClass(param);
    SelectedItemEventHandler X_OnReceived = (s, e) =>
    {
        Debug.WriteLine(e.ReceivedItem);
        _waitCTS.Cancel();
    }

    x.OnReceived += X_OnReceived;


    try
    {
        await Task.Delay(5000, _waitCTS.Token).ConfigureAwait(true);
    }
    catch (TaskCanceledException)
    {
        Debug.WriteLine("Timed out");
    }

    x.OnReceived -= X_OnReceived;
    x.Cleanup();
}
2赞 Evk 1/19/2023 #2

你可以完全不做,例如:CancellationTokenSourceTaskCompletionSource

async Task WaitWithTimeout()
{
    var tcs = new TaskCompletionSource<string>();
    XClass x = new XClass();
    // declare handler as local variable here
    Action<string> handler = (s) =>
    {
        // complete the task
        tcs.TrySetResult(s);
    };

    x.OnReceived += handler;
    // now check which task completes first - the one from handler, or delay 5s
    var completedFirst = await Task.WhenAny(tcs.Task, Task.Delay(5000));
    x.OnReceived -= handler;
    if (completedFirst == tcs.Task)
    {
        // got the result here
        Console.WriteLine(tcs.Task.Result);
    }
    else
    {
        Console.WriteLine("Timed out");
    }
}

class XClass
{
    public event Action<string> OnReceived;
}