如何正确启动和取消任务.

Rok*_*545 1 c# multithreading asynchronous task async-await

我似乎无法理解如何正确启动任务并取消它.我已经改变了我的代码,以便更容易理解我的问题并将其粘贴在下面(我还添加了一些注释).SubscribeToReport首先调用该方法 - 它包含一个等待runReportTask,从我的理解中将启动该runReportTask方法,等待它完成执行,然后继续执行之后出现的代码await.

在里面runReportTask我创建一个CancellationTokenSource并使用开始一个新的任务Task.Factory.StartNew().在任务内部,只要任务尚未取消,就会有一个while循环执行处理.根据我的理解,如果cancellationToken.IsCancellationRequested是真的,我的while循环中的else将运行,任务将停止.在那之后,我希望SubscribeToReport()继续执行下去-这不会发生,并且它不会永远看起来像我的代码做任何事情,当我打电话cancellationTokenSource.Cancel()StopReportTask().我究竟做错了什么?我已经有一段时间了,我似乎无法理解这应该如何运作.

private async static void SubscribeToReport()
{
    Console.WriteLine("Waiting for task to finish or cancel...");

    await runReportTask();

    Console.WriteLine("Task has finished or was canceled."); // THIS LINE NEVER EXECUTES
}

private static CancellationTokenSource cancellationTokenSource;
private static CancellationToken cancellationToken;
private bool moreToDo = true;
private static Task runReportTask()
{
    cancellationTokenSource = new CancellationTokenSource();
    cancellationToken = cancellationTokenSource.Token;

    return Task.Factory.StartNew(() =>
    {
        // Were we already canceled?
        cancellationToken.ThrowIfCancellationRequested();

        while(moreToDo == true)
        {
            if(!cancellationToken.IsCancellationRequested)
            {
                // Do important code here
            }
            else{
                Console.WriteLine("Task canceled."); // THIS LINE NEVER EXECUTES WHEN CALLING StopReportTask() below
                cancellationToken.ThrowIfCancellationRequested();
                break;
            }
        }

    }, cancellationToken);
}

// This method is called from another source file
private void StopReportTask()
{
    Console.WriteLine("Stopping report task...");

    cancellationTokenSource.Cancel();
}
Run Code Online (Sandbox Code Playgroud)

Ste*_*ary 5

它包含一个await runReportTask,根据我的理解,它将启动runReportTask方法,等待它完成执行,然后继续执行await后出现的代码.

代码:

await runReportTask();
Run Code Online (Sandbox Code Playgroud)

就像这段代码:

var task = runReportTask();
await task;
Run Code Online (Sandbox Code Playgroud)

因此,要清楚,正是方法调用启动任务运行.await只是(异步)等待它完成.

同样重要的是要注意这await异步等待.这意味着它会暂停方法的执行并返回.因此,就您的调用代码所知,SubscribeToReport已完成执行.

(这是其中一个问题async void ;它不易消耗 - 调用代码不知道何时完成).

在runReportTask内部我创建一个CancellationTokenSource并使用Task.Factory.StartNew()启动一个新任务.

作为旁注,你应该使用Task.Run.Task.Factory.StartNew是具有危险默认设置的低级API.

当我在StopReportTask()中调用cancellationTokenSource.Cancel()时,我的代码似乎没有做任何事情

这可能是一个不同的cancellationTokenSource例子.

你可以尝试这个,作为一个测试 - 我不确定它是否是你想要的语义,但它会导致runReportTask取消之前的尝试:

private static CancellationTokenSource cancellationTokenSource;
private static readonly object _mutex = new object();
private bool moreToDo = true;
private static Task runReportTask()
{
  CancellationTokenSource oldCts, currentCts;
  lock (_mutex)
  {
    oldCts = cancellationTokenSource;
    currentCts = cancellationTokenSource = new CancellationTokenSource();
  }
  if (oldCts != null)
    oldCts.Cancel();
  var cancellationToken = currentCts.Token;
  ...
}
Run Code Online (Sandbox Code Playgroud)