在另一个 class C# 中取消任务

Cancel task in another class C#

我对 C# 中的 cancellationTokenSource 有疑问

public class Building {
    public CancellationTokenSource BuildTokenSource;

    public void StartBuilt()
    {
        BuildTokenSource = new CancellationTokenSource();

        buildingService.buildTask = Task.Run(async () =>
        {
            await clock.Delay(BUILT_TIME);
        }, BuildTokenSource.Token);
    }

    public void CancelBuilt()
    {
        if (BuildTokenSource != null)
        {
            BuildTokenSource.Cancel();
        }
    }
}

在另一个 class 中,想要检测任务是否像这样取消,但它不起作用。从未触发的捕获异常

public async Task<Building> GetBuildingOfUserTask()
    {
        double remainingTime = unitService.GetRemainingTime();

        if (remainingTime <= 2000 && remainingTime > 0.0)
        {
            Building building = GetBuilding();
            CancellationToken cancellation = building.BuildTokenSource.Token;
            try
            {
                await buildTask;
            }
            catch (OperationCanceledException) when (cancellation.IsCancellationRequested)
            {
                return GetBuildingOfUser();
            }
        }
        return GetBuildingOfUser();
    }

有人知道为什么这不起作用,在这种情况下是解决方案吗?

我没有看到任何地方调用 CancelBuilt() + 你必须调用 BuildTokenSource.Token.ThrowIfCancellationRequested() 才能引发 OperationCanceledException 异常

应该在使用 cancelationToken 的异步方法之外调用 token.Cancel()。同样使用异步方法必须调用(通常在每一步)Token.ThrowIfCancellationRequested();

我相信使用 Mediator (Mediator design pattern) 会更合适。这实际上是一个发布-订阅模型,它将在取消时发布一个事件并通知所有订阅者。

第一个class将中介实例作为参考(一个只读字段,一个属性)来发布取消事件,另一个应该相同的实例 作为参考,以便在事件实际发生时得到通知。还有一点你应该注意的是当包含'GetBuildingOfUserTask'方法的class实例被销毁时应该取消订阅。

你怎么看?

clock.Delay(BUILT_TIME) 是否有接受 CancellationToken 的重载?如果是这样,请使用它。

问题是当您取消时,如果代码已经在等待 clock.Delay(BUILT_TIME)clock.Delay 将不知道它需要抛出异常。

我同意 HadascokJ 的回答,我想带来更多的启发。

您有一个主要任务在 Task buildingService.buildTask 开始,它的次要任务在 await clock.Delay(BUILT_TIME);

开始

第一个任务管理 CancellationToken 但从属的任务不管理。为了更好地计算它,请将您的 clock.Delay(BUILT_TIME) 替换为 Task Task.Delay(int millisecondsDelay, CancellationToken cancellationToken);,当然还要提供 CancelationToken。您会看到,在这种情况下,从属任务将被取消。同时调用 void CancellationToken.CancelAfter(int millisecondsDelay)

由于您没有提供CancellationToken给从属任务,所以主任务已经启动,主任务和从属任务都不会被取消。

另一方面,要取消从属任务的执行,为从属任务提供一些逻辑来管理 CancelationToken 到其相应的方法中,并在必要时调用 CancelationToken.ThrowIfCancellationRequested(),抛出 OperationCanceledException.

至少,尝试将长任务拆分成几个小任务。 我用来管理异步。任务,这些任务必须 运行 按顺序放入能够观察这些 TaskStatus 的任务队列中。为了解决这个问题,如果您需要,我在 github 有一个实现。我称之为FifoTaskQueue