在async/await中取消线程

Kei*_*ows 1 .net c# async-await

我正在尝试测试一个使用async/await处理事务使用await/async轮询功能的小应用程序.

我的测试设置:

    [TestMethod]
    public void TestProcessTimerOnly()
    {
        // this initializes and kicks off the polling
        var tp = new TransactionProcessor();

        try
        {
            Thread.Sleep(5000);
            tp.CancelProcessing();
        }
        catch (Exception ex)
        {
            LogErrors(ref tp, ex);
        }
        finally
        {
            DisplayLog(tp);
        }
    }

    [TestMethod]
    public void TestProcessTimerOnlyForcedCancellation()
    {
        // this initializes and kicks off the polling
        var tp = new TransactionProcessor(1);

        try
        {
            Thread.Sleep(5000);
            tp.CancelProcessing();
        }
        catch (Exception ex)
        {
            LogErrors(ref tp, ex);
        }
        finally
        {
            DisplayLog(tp);
        }
    }
Run Code Online (Sandbox Code Playgroud)

我的代码(全部在一个类中):

    // Constructor
    public TransactionProcessor(int? debugForcedCancellationDelay = null)
    {
// >>>>>>>> Setup Cancellation <<<<<<<<
        if(debugForcedCancellationDelay.IsEmpty() || debugForcedCancellationDelay.IsZeroOrLess())
            _cancellationToken = new CancellationTokenSource();
        else
            _cancellationToken = new CancellationTokenSource(TimeSpan.FromSeconds(debugForcedCancellationDelay.Value));
// >>>>>>>> End <<<<<<<<

// was:
        // RepeatActionEvery(() => TestingLog.Add("Repeat Action Every 1 Second"), TimeSpan.FromSeconds(1), _cancellationToken.Token).Wait();
// corrected:
        // _processTask is defined as a global field of type Task...
        _processTask = RepeatActionEvery(() => TestingLog.Add("Repeat Action Every 1 Second"), TimeSpan.FromSeconds(1), _cancellationToken.Token);    //.Wait();
    }

// was:
    //public void CancelProcessing()
// corrected:
    public async Task CancelProcessing()
    {
        _cancellationToken.Cancel();
        await _processTask;
    }
    public static async Task RepeatActionEvery(Action action, TimeSpan interval, CancellationToken cancellationToken)
    {
        while (true)
        {
            action();
            var task = Task.Delay(interval, cancellationToken);

            try { await task; }
            catch (TaskCanceledException) { return; }
        }
    }
Run Code Online (Sandbox Code Playgroud)

当我运行TestProcessTimerOnly()测试时,它会坐在那里直到我最终取消测试装备.

当我运行TestProcessTimerOnlyForcedCancellation()测试时,它的行为符合预期.

所以问题归结为:我是否正确使用_cancellationToken变量?在一个实例中,我使用超时参数初始化它.在另一个例子中,我初始化它没有参数.我在这做错了什么?

i3a*_*non 6

你正在使用"同步异步",这是非常气馁的.

你在做什么

您将获得一项任务,RepeatActionEvery只有在取消令牌被取消时才会结束.但是你正在等待该任务的同步(阻塞),这意味着你永远不会离开构造函数并到达取消令牌(tp.CancelProcessing();)的行.

当然,当你创建CancellationTokenSource一个超时时,它最终将取消自己,而不必调用它,所以任务将结束,等待它的线程将可以自由地完成构造函数和调用tp.CancelProcessing();

你想做什么

您应该做的事情(IIUC)是存储事务任务而不等待它,并且仅在您取消(或完成)事务时等待:

public TransactionProcessor(int? debugForcedCancellationDelay = null)
{
    // ...
    Task = RepeatActionEvery(
        () => TestingLog.Add("Repeat Action Every 1 Second"), 
        TimeSpan.FromSeconds(1), 
        _cancellationToken.Token);
}

public async Task CancelProcessingAsync()
{
    _cancellationToken.Cancel();
    await Task;
}
Run Code Online (Sandbox Code Playgroud)