如何对具有最新上下文的多个并发任务执行一次取消/异常清理?

How do I perform cleanup of cancellation/Exceptions on multiple Concurrent Task's with newest context once?

提问人:The_Matrix 提问时间:8/20/2023 最后编辑:The_Matrix 更新时间:8/20/2023 访问量:35

问:

我正在编写一个桌面应用程序来控制一个小型 XY 阶段,遵循 MVP 架构。有一个 UI 线程,该线程发送到使用它们的 Operation 线程,并执行实际控制。Operation 线程在使用新事件时并发运行其硬件控制逻辑。存在覆盖当前任务(例如停止然后开始执行)的一个或多个新事件的情况。我希望能够取消任务、执行特定的清理和处理错误,例如使用操作线程中提供的最新上下文。ViewEventViewEvent.StopViewEvent.MoveRightViewEvent.MoveLeftViewEventLostConnectionException

我尝试运行一个循环,该循环从 a 读取并执行 's without 并利用它来处理取消/失败清理。生成任务的业务逻辑将在 Presenter 内部执行。asyncBlockingCollection<ViewEvent>async TaskawaitTask.ContinueWith

public async Task HandleEventsBlocking()
{
    foreach (var viewEvent in _viewEventsQueue.GetConsumingEnumerable())
    {
        switch (viewEvent)
        {
            case ViewEvent.Start:
                Presenter.HandleStartAsync(_cts.Token).ContinueWith((task) =>
                {
                    if (task.IsFaulted)
                    {
                        // Clean up and cancel all other tasks.
                        foreach (var innerException in task.Exception.InnerExceptions)
                        {
                            Console.WriteLine($"Caught exception: {innerException.Message}");
                        }
                        _cts.Cancel();
                    }
                    else if (task.IsCanceled)
                    {
                        // Clean up.
                        await Presenter.CleanUp();
                    }
                });
                break;
            case ViewEvent.Faulty:
                Presenter.HandleFaultyAsync(_cts.Token).ContinueWith((task) =>
                {
                    if (task.IsFaulted)
                    {
                        // Clean up and cancel all other tasks.
                        foreach (var innerException in task.Exception.InnerExceptions)
                        {
                            Console.WriteLine($"Caught exception: {innerException.Message}");
                        }
                        _cts.Cancel();
                    }
                    else if (task.IsCanceled)
                    {
                        // Clean up.
                        await Presenter.CleanUp();
                    }

                });
                break;
            case ViewEvent.Stop:
                _cts.Cancel();
                break;
            case ViewEvent.Reset:
                _cts.Dispose();
                _cts = new CancellationTokenSource();
                break;
        }
    }
}

public async Task HandleStartAsync(CancellationToken token)
{
    await Task.Run(async () =>
    {
        token.ThrowIfCancellationRequested();
        await Task.Delay(2000); // Simulated work. Would be passed token.
        token.ThrowIfCancellationRequested();
    });
}

public async Task HandleFaultyAsync(CancellationToken token)
{
    await Task.Run(async () =>
    {
        token.ThrowIfCancellationRequested();
        await Task.Delay(2000); // Simulated work that failed. Would be passed token.
        throw new InvalidOperationException("FAILED ON PURPOSE");
    });
}

但是,这种方法不起作用,因为每个人都可能会执行重复的清理工作。async Task

将添加一个额外的布尔标志,该标志在 之前运行,并在所有执行时使用 退出,然后执行清理。该标志将因失败而设置一次,随后将跳过设置它。PerformCleanUpswitchTask.WaitAllasync TaskTaskContinueWith

C# 异步 错误处理 async-await

评论

2赞 Theodor Zoulias 8/20/2023
似乎“操作线程”()不是线程,而是异步流(方法)。这种方法似乎也不做任何实际工作,而只是安排要在 上完成的工作。这使得人们费解为什么这种方法甚至存在。可以直接从 UI 线程完成调度工作,而不会造成任何冻结或其他不利的副作用。作为一个对你的场景没有深刻理解的外部观察者,整个架构对我来说就像一个徒劳的练习。HandleEventsBlockingasyncThreadPoolThreadPool
1赞 The_Matrix 8/20/2023
@TheodorZoulias我以前从未编写过桌面应用程序,到目前为止,我所做的一切都是小想法的小测试应用程序。您的分析是正确的,操作线程什么也没做,这让我质疑它的存在。这也是我的第一个多线程应用程序,所以我正在学习反复试验。
2赞 The_Matrix 8/20/2023
@TheodorZoulias 谢谢你帮助我回答我的问题。现在我更仔细地检查了应用程序,我意识到我有很多过度复杂化的问题。最初,我在UI中调用了所有业务逻辑,但我想要一个更好的架构,这就是我追求UI/Operation线程分离的原因。背后的代码是有效的,但无法管理,但现在我看到我应该将其抽象为对象而不是线程。感谢您的时间和帮助。async/awaitasync/await

答: 暂无答案