C#任务 - 链接的取消令牌无效

时间:2016-11-10 09:34:26

标签: c# task-parallel-library task cancellationtokensource cancellation-token

请有人帮助我。 我试图使用TPL链接取消令牌。问题是取消主要的CancellationTokenSource后,链接令牌的属性IsCancellationRequested的值仍然是" false"。

我开始完成两项任务,只是为了确定 - 但它应该是同一件事。第一个我通过CancellationToken,第二个我通过CancellationTokenSource。行为是相同的:在while循环中 - 条件linkedToken.IsCancellationRequested保持" false"取消后。

以下是我正在使用的代码:

C:\Users\someUser\AndroidStudioProjects

从控制台应用程序主方法启动此代码:

public class Manager
{
    private Task tokenTask;
    private Task sourceTask;
    private CancellationTokenSource mainCancelationTokenSource;
    private CancellationToken mainToken;

    public Manager()
    {
        this.mainCancelationTokenSource = new CancellationTokenSource();
        this.mainToken = mainCancelationTokenSource.Token;
        this.mainToken.Register(MainCanceled);
    }

    public void Start()
    {
        Workers w = new Workers();
        tokenTask = Task.Run(() => w.DoWorkToken(mainToken), mainToken);
        sourceTask = Task.Run(() => w.DoWorkSource(mainCancelationTokenSource), mainCancelationTokenSource.Token);

    }
    public void Cancel()
    {
        mainCancelationTokenSource.Cancel();
    }

    private void MainCanceled()
    {
        try
        {
            tokenTask.Wait();
        }
        catch (Exception e)
        {

        }

        try
        {
            sourceTask.Wait();
        }
        catch (Exception e)
        {

        }
    }
}

class Workers
{
    public void DoWorkToken(CancellationToken mainToken)
    {
        CancellationTokenSource linkedCts = CancellationTokenSource.CreateLinkedTokenSource(mainToken);
        CancellationToken linkedToken = linkedCts.Token;

        while (!linkedToken.IsCancellationRequested)
        {
            Random r = new Random();
            Task.Delay(200 * r.Next(1, 11)).Wait();
        }

        linkedToken.ThrowIfCancellationRequested();
    }

    public void DoWorkSource(CancellationTokenSource mainCts)
    {
        CancellationTokenSource linkedCts = CancellationTokenSource.CreateLinkedTokenSource(mainCts.Token);

        while (!linkedCts.Token.IsCancellationRequested)
        {
            Random r = new Random();
            Task.Delay(200 * r.Next(1, 11)).Wait();
        }

        linkedCts.Token.ThrowIfCancellationRequested();
    }
}

感谢您的帮助!

1 个答案:

答案 0 :(得分:5)

这个问题的根源是这一行:

this.mainToken.Register(MainCanceled);

注册一个回调以在取消令牌时执行。在内部,回调被委托给父CancellationTokenSource,并被放入一些回调列表中,以便在请求源取消时执行。在那个处理程序中你做

tokenTask.Wait();
sourceTask.Wait();

因此,在相关任务完成之前,此回调将无法完成。 在任务中你这样做

while (!linkedToken.IsCancellationRequested) {
    // loop here
}

因此,在请求取消之前,任务将无法完成。

现在到了棘手的部分:当你创建链接令牌源

CancellationTokenSource.CreateLinkedTokenSource(mainToken)

将回调放入父(mainToken)令牌源列表中。执行此回调时 - 链接的令牌源也会被取消。在您的回调列表

之后,此回调将

所以你死了,因为第一个回调(你的)等待任务完成,任务等待链接令牌IsCancellationRequested等于true,链接令牌源等待它自己的回调完成设置这个标志。

要解决此问题,只需删除回调,或以不阻止等待相关任务完成的方式编写回调,或将任务更改为不等待链接令牌源IsCancellationRequested标志