C#backgroundWorker取消并调用

时间:2017-10-06 17:16:09

标签: c# backgroundworker invoke cancellation

我有2个关于backgroundWorker的问题:一个是取消,另一个是调用。

我的代码简要如下:

public partial class App : Form {
    //Some codes omitted
    public EditProcess Process = new EditProcess(ProcessTextBox);

    private void ExecuteBtn_Click (object sender, EventArgs e) {
        //DnldBgWorker is a backgroundWorker.
        Download Dnld = new Download(dir, Process);
        DnldBgWorker.DoWork += (obj, e) => GoDownload(Dnld, urllist, e);
        DnldBgWorker.RunWorkerAsync();
        DnldBgWorker.RunWorkerCompleted += (obj, e) => FinishExecution();
    }

    private void GoDownload(Download Dnld, string[] urllist, EventArgs e) {
        foreach(string url in urllist) {
            Dnld.Dnld(url);
        }

        for (int i = 0; i < 10; i++) {
            System.Threading.Thread.Sleep(50);
                if (DnldBgWorker.CancellationPending) {
                    e.Cancel = true;
                    return;
            }
        }
    }

    private void StopBtn_Click(object sender, EventArgs e) {
        DnldBgWorker.CancelAsync();
    }
}

public class Download {
    // Some codes omitted
    public WebClient client = new WebClient();
    public EditProcess Process;

    public Download(string dir, EditProcess Process) {
        this.dir = dir;
        this.Process = Process;
    }

    public void Dnld() {
        client.DownloadFile(url, dir);
        EditProcess.Text(String.Format("Downloaded: {0}\r\n"));
    }
}

public class EditProcess {
    public TextBox Box;

    public EditProcess(TextBox Box) {
        this.Box = Box;
    }

    public void Text(string textToAdd) {
        Box.Text += textToAdd;
    }
}

首先,当DnldBgWorker正在运行时,我点击了StopBtn来停止DnldBgWorker,异步工作不会停止。我该如何停止DnldBgWorker

其次,EditProcess.Text(String.Format("Downloaded: {0}\r\n"));会给我一个错误,即跨线程操作无效。我知道我应该让代表这样做,但我不确切知道如何。

++)我的代码看起来像是以非常复杂的方式做非常简单的工作,但我在这段代码中放了非常重要的元素,所以请理解

4 个答案:

答案 0 :(得分:0)

这里有两个问题:

关于取消 - 您需要在下载循环中检查取消状态(因此只下载部分请求的文件),而不是在我不太了解的后续循环中。

作为附加说明,您可以使用WebClient.DownloadFileAsyncWebClient.CancelAsync组合来避免使用BackgroundWorker。

截至报告进度 - 让您BackgroundWorker通过ReportProgress向UI线程报告进度,并从那里更新用户界面。

答案 1 :(得分:0)

至于如何取消线程。这是一个控制台应用程序的基本示例,我希望您能够适应更复杂的代码。

void Main()
{
    var tokenSource = new CancellationTokenSource();
    System.Threading.Tasks.Task.Run(() => BackgroundThread(tokenSource.Token));

    Thread.Sleep(5000);
    tokenSource.Cancel();   
}

private void BackgroundThread(CancellationToken token)
{
    while (token.IsCancellationRequested == false) {
        Console.Write(".");
        Thread.Sleep(1000);
    }

    Console.WriteLine("\nCancellation Requested Thread Exiting...");
}

结果将如下。

.....
Cancellation Requested Thread Exiting...

其次,就如何从您的线程调用与用户界面进行交互一样,希望这个博客能为您提供帮助。 Updating Windows Form UI elements from another thread

如果您觉得有帮助,请告诉我。

答案 2 :(得分:0)

要支持取消,您需要设置属性

 DnldBgWorker.WorkerSupportsCancellation = true;

目前尚不清楚您是否将其设置在其他位置,但您需要它来取消后台工作程序,因为您可以在MSDN上阅读

  

如果需要,请将WorkerSupportsCancellation属性设置为true   BackgroundWorker支持取消。当此属性为true时,   您可以调用CancelAsync方法来中断后台   操作

另外我会将GoDownload方法更改为

private void GoDownload(Download Dnld, string[] urllist, EventArgs e) 
{
    foreach(string url in urllist) 
    {
        Dnld.Dnld(url);

        // this is just to give more time to test the cancellation
        System.Threading.Thread.Sleep(500);

        // Check the cancellation after each download
        if (DnldBgWorker.CancellationPending) 
        {
            e.Cancel = true;
            return;
        }
    }
}

对于第二个问题,当代码在UI线程上运行而不是在后台线程中运行时,需要调用该方法。您可以轻松地在ProgressChanged事件的事件处理程序中实现此移动文本框更新。要设置事件处理程序,您需要将另一个属性设置为true

DnldBgWorker.WorkerReportsProgress = true;

并设置ProgressChanged事件的事件处理程序

DnldBgWorker.ProgressChanged += DnldBgWorker_ProgressChanged;

private void DnldBgWorker_ProgressChanged(object sender,    ProgressChangedEventArgs e)
{
    EditProcess.Text(String.Format("Downloaded: {0}\r\n", e.ProgressPercentage));
}

并使用

在GoDownload中引发此事件
DnldBgWorker.ReportProgress(i);

答案 3 :(得分:0)

让我们在进入代码之前解决问题

  1. 出于某种原因,在实际下载完成后,您有一个完全冗余的循环等待取消。因此BtnStop不适合您
  2. 当您从BackgroundWorker上下文中调用的EditProcess.Text调用Dnld时,您正在从不“拥有”它的线程中访问GUI元素。您可以详细了解cross-thread operation here。在您的情况下,您应该通过ReportProgress来电。
  3. 现在你可以看到我的方式

    1. 在将GoDownload检查移至下载循环时,从if (DnldBgWorker.CancellationPending)删除了冗余循环。这应该使StopBtn现在正常工作。
    2. 添加ProgressChanged事件处理程序以在ExecuteBtn_Click中执行GUI更改。这是由DnldBgWorker.ReportProgress方法的下载循环中的GoDownload调用触发的。在这里,我们将自定义格式的字符串传递为UserState
    3. 同时确保您已启用ReportsProgressSupportsCancellation属性,如下所示,可能在您的设计器属性框中或代码lile DnldBgWorker.WorkerReportsProgress = true; DnldBgWorker.WorkerSupportsCancellation = true;
    4. 希望其他一切都清楚,下面的代码。

      public partial class App : Form {
          //Some codes omitted
          public EditProcess Process = new EditProcess(ProcessTextBox);
      
          private void ExecuteBtn_Click (object sender, EventArgs e) {
              //DnldBgWorker is a backgroundWorker.
              Download Dnld = new Download(dir, Process);
              DnldBgWorker.DoWork += (obj, e) => GoDownload(Dnld, urllist, e);
              DnldBgWorker.RunWorkerAsync();
              DnldBgWorker.RunWorkerCompleted += (obj, e) => FinishExecution();
              DnldBgWorker.ProgressChanged += (s, e) => EditProcess.Text((string)e.UserState);;
          }
      
          private void GoDownload(Download Dnld, string[] urllist, EventArgs e) {
              foreach(string url in urllist) {
                  Dnld.Dnld(url);
                  DnldBgWorker.ReportProgress(0, String.Format($"Downloaded: {url}\r\n"));
                  if (DnldBgWorker.CancellationPending) {
                      e.Cancel = true;
                      return;
                  }
              }
          }
      
          private void StopBtn_Click(object sender, EventArgs e) {
              DnldBgWorker.CancelAsync();
          }
      }
      
      public class Download {
          // Some codes omitted
          public WebClient client = new WebClient();
          public EditProcess Process;
      
          public Download(string dir, EditProcess Process) {
              this.dir = dir;
              this.Process = Process;
          }
      
          public void Dnld() {
              client.DownloadFile(url, dir);
          }
      }
      
      public class EditProcess {
          public TextBox Box;
      
          public EditProcess(TextBox Box) {
              this.Box = Box;
          }
      
          public void Text(string textToAdd) {
              Box.Text += textToAdd;
          }
      }