取消异步上传任务

时间:2016-05-01 10:21:59

标签: c# asynchronous async-await

我通过一种方法获得Uploader课程 - Upload

public static int Upload(string endpoint,object objectToBeUploaded)
    {
        Source.Token.ThrowIfCancellationRequested();
        var repos = new UploadRepository(endpoint);
        return repos.Upload(objectToBeUploaded);
    }

Source是项目中可用的静态CancellationTokenSource

我还有一个我需要上传某个object的端点列表。

Form中的代码(它是使用WinForms的非常小的项目)如下所示:

private async Task UploadObjectAsync(
            string endpoint,
            object objectToBeUploaded)
     {
        try
        {
            int elementId  = await Task.Factory.StartNew(
                        () => Uploader.Upload(endpoint,objectToBeUploaded));
           //do something with the returned value..
        }
        catch(OperationCanceledEception ex)
        {
          //handle the exception..
        }
     }

然后我像这样设置btnUpload.Click处理程序,以便以后可以使用它:

this.btnUpload.Click += async (s, e) =>
{
   foreach(var endpoint in endpoints)
   {
       await UploadObjectASsync(endpoint,someObject);
   }
}

问题在于,无论何时我开始上传到所有端点(如何获取它们都无关紧要),我决定使用Source.Cancel();取消上传过程,第一个UploadObjectAsync将始终通过 Source.Token.ThrowIfCancellationRequested();方法中的Upload检查已通过。其余任务将被正常取消并妥善处理。

如何重新构建此代码,以确保第一个UploadObjectAsync Task也会被取消?

值得一提的是,我也无法访问上传过程本身的源代码(服务参考) - repos.Upload(objectToBeUploaded)方法中的Upload

3 个答案:

答案 0 :(得分:1)

你无能为力。 Upload方法不接受令牌。当您点击取消按钮时,第一个任务已经通过了取消检查。您可以向自己证明取消是一个时间问题,如果取消呼叫,则在投掷前增加10秒睡眠。然后所有任务都将取消。

答案 1 :(得分:1)

您需要使UploadRepository.Upload采用CancellationToken。 特别是当那是那个进行I / O操作的时候。那就是async/await确实付出的代价。

这也有助于你摆脱它:Task.Factory.StartNew,因为Upload方法已经返回任务。没有必要分拆任务。

在您当前的设置中,如果有足够的时间启动任务(并通过ThrowIfCancellationRequested),您将无法取消任何上传。即使需要30秒。

另外,您可能感兴趣:Task.Run

答案 2 :(得分:0)

问题是你无法阻止Upload函数内发生的进程,除非它检查CancellationToken任何终止本身的状态。

所以你可以做的是通过这样做来中止正在执行的线程:

int elementId  = await Task.Factory.StartNew(() => 
{
  try
  {
    using (Source.Token.Register(Thread.CurrentThread.Interrupt))
    {
      return Uploader.Upload(endpoint, objectToBeUploaded));
    }
  }
  catch (ThreadInterruptedException ex)
  {
     throw new OperationCanceledEception(ex)
  }
}, Source.Token);

使用Source.Token.Register(delegate)函数可以在令牌被取消时使令牌调用该函数。这样,当前正在执行上传的线程就会立即抛出异常。

此方法仅在线程不时进入 WaitSleepJoin -State的情况下有效,因为只有在线程处于该状态时才会引发异常。请查看Thread.Interrupt函数的文档。

另一种方法是使用Thread.AbortThreadAbortedException。这会在任何情况下终止你的线程,但它可能会破坏你的服务的内部状态,因为线程保持的锁定将无法正常释放。所以使用这种方法要非常小心。