【问题标题】:Cancelling a task which retrieves URLs asynchronously取消异步检索 URL 的任务
【发布时间】:2012-01-31 04:34:07
【问题描述】:

我在找出如何在 C# 中取消此任务时遇到了一些问题。我对处理线程并没有很深的理解,我尝试在谷歌上搜索一些简单的代码示例来帮助我,但我真的无处可去。这是我正在处理的代码:

var tasks = urls.Select(url => Task.Factory.StartNew(
state =>
{
    using (var client = new WebClient())
    {

        lock (this)
        {

        // code to download stuff from URL

        }

    }
}, url)).ToArray();

    try
    {
       Task.WaitAll(tasks);
    }
    catch (Exception e)
    {
      textBox2.AppendText("Error: " + e.ToString());
    }

其中“urls”是一个 URL 数组。有没有一种简单的方法可以在我单击程序中的按钮时完全停止 URL 的下载?此外,我粘贴的代码 sn-p 位于 backgroundWorker1 调用的函数中,我想这可能会使事情变得更复杂一些。 (我有一个 backgroundWorker 的原因是 UI 在下载 URL 时不会锁定。)

如果这有点令人困惑,这里是我试图用我的代码实现的大纲:

  • 我有一个 URL 数组,我想异步下载每个 URL 而不会锁定 UI。
  • 我希望用户通过单击按钮来停止程序下载 URL,这几乎是取消线程。
  • 当用户再次单击该按钮时,程序会从该数组中重新下载所有 URL。

提前致谢。

【问题讨论】:

  • 请不要在问题标题中包含标签;它只是为我们这些关心 SO 一致性的人做更多的工作。

标签: c# asynchronous multitasking task


【解决方案1】:

不知道这是否是正确的方法,但我已经能够使用以下代码取消任务。我已经用ListBoxProgressBar 创建了一个表单,所以我提出并处理BackgroundWorkerProgressChanged 事件。希望这对您有所帮助。

void bw_DoWork(object sender, DoWorkEventArgs e)
{
    CancellationTokenSource _tokenSource = new CancellationTokenSource();
    CancellationToken _token = _tokenSource.Token;

    var urls = e.Argument as IEnumerable<string>;

    _token = new CancellationToken();

    if (urls == null) return;

    var i = 0;
    var a = 100 / urls.Count();

    var sb = new StringBuilder();
    var t = urls.Select(url => Task.Factory.StartNew(
                    (u) =>{
                        using (var wc = new WebClient())
                        {
                            lock (this){
                                var s = wc.DownloadString(u.ToString());
                                sb.AppendFormat("{1}:{0}\r\n", "", u);
                            }
                        }

                    if (Worker.CancellationPending){
                        //MAGIC HAPPENS HERE, IF BackgroundWorker IS REQUESTED
                        //TO CANCEL, WE CANCEL CancellationTokenSource
                        _tokenSource.Cancel();
                    }

                    //IF CANCELATION REQUESTED VIA CancellationTokenSource
                    //THROW EXCEPTION WHICH WILL ADD TO AggreegateException
                    _token.ThrowIfCancellationRequested();

                    //YOU CAN IGNORE FOLLOWING 2 LINES
                    i += a;
                    Worker.ReportProgress(i, u);
    }, url, _token)).ToArray();

    try
    {
        Task.WaitAll(t);
    }
    catch (AggregateException age)
    {
        if (age.InnerException is OperationCanceledException)
            sb.Append("Task canceled");
    }
    catch (Exception ex)
    {
        sb.Append(ex.Message);
    }

    e.Result = sb;
}

【讨论】:

  • 哇,那个代码示例非常有用,我认为我自己的一段代码有效,并且线程被成功取消。不过我不想太激动,总有事情发生。 :)
【解决方案2】:

使用 WebClient,您可以使用 CancelAsync 方法取消异步操作。

要取消您通过Factory.StartNew 开始的任务,您应该使用CancellationTokenSource。您需要将CancellationTokenSource.Token 传递给任务(您可以使用token.IsCancellationRequested 询问令牌是否已被取消),并且您可以调用CancellationTokenSource.Cancel() 将令牌设置为已取消。

【讨论】:

  • 干杯,关于Factory.StartNew,我会确保记住这一点
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多