我有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"));
会给我一个错误,即跨线程操作无效。我知道我应该让代表这样做,但我不确切知道如何。
++)我的代码看起来像是以非常复杂的方式做非常简单的工作,但我在这段代码中放了非常重要的元素,所以请理解
答案 0 :(得分:0)
这里有两个问题:
关于取消 - 您需要在下载循环中检查取消状态(因此只下载部分请求的文件),而不是在我不太了解的后续循环中。
作为附加说明,您可以使用WebClient.DownloadFileAsync
和WebClient.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)
让我们在进入代码之前解决问题
BtnStop
不适合您EditProcess.Text
调用Dnld
时,您正在从不“拥有”它的线程中访问GUI元素。您可以详细了解cross-thread operation here。在您的情况下,您应该通过ReportProgress
来电。现在你可以看到我的方式
GoDownload
检查移至下载循环时,从if (DnldBgWorker.CancellationPending)
删除了冗余循环。这应该使StopBtn
现在正常工作。ProgressChanged
事件处理程序以在ExecuteBtn_Click
中执行GUI更改。这是由DnldBgWorker.ReportProgress
方法的下载循环中的GoDownload
调用触发的。在这里,我们将自定义格式的字符串传递为UserState
ReportsProgress
和SupportsCancellation
属性,如下所示,可能在您的设计器属性框中或代码lile DnldBgWorker.WorkerReportsProgress = true; DnldBgWorker.WorkerSupportsCancellation = true;
希望其他一切都清楚,下面的代码。
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;
}
}