Understanding BackgroundWorker
本文关键字:BackgroundWorker Understanding | 更新日期: 2023-09-27 17:50:48
我想了解BackgroundWorker
是如何使用的。我试图将其分解为一个控制台c#项目,实际上它是一个Windows窗体应用程序,其中一个按钮触发三个后台任务的执行。当按钮被按下时,它应该被禁用,以防止进一步的按钮事件。当所有三个任务都完成后,应该再次启用该按钮。另外,这三个任务的成功与否也应该在主线程中进行测试。为了避免将所有这些都混合到表单应用程序中,我现在正在尝试理解基础知识并将其转移到表单应用程序中。(从评论设置你可以猜到我的理解问题在哪里)考虑这段(仍然是错误的)代码:
using System;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Threading;
namespace BGWorkerConsoleApp
{
class Program
{
// #region BackgroundWorker
//
private System.ComponentModel.BackgroundWorker worker = new System.ComponentModel.BackgroundWorker();
//
// #endregion
static void Main(string[] args)
{
// BackgroundWorker worker = new System.ComponentModel.BackgroundWorker();
worker.RunWorkerCompleted += TestComplete;
worker.DoWork += TestConnection1;
// worker.DoWork += TestConnection2;
// worker.DoWork += TestConnection3;
DoWorkEventArgs e = new DoWorkEventArgs(); // ???
worker.RunWorkerAsync(e); // ???
}
private void TestConnection1(object sender, DoWorkEventArgs e)
{
bool success = false;
Thread.Sleep(14000); // stands for some time consuming Task
success = true;
e.Result = success;
}
private void TestConnection2(object sender, DoWorkEventArgs e)
{
bool success = false;
Thread.Sleep(10000); // stands for some time consuming Task
success = true;
e.Result = success;
}
private void TestConnection3(object sender, DoWorkEventArgs e)
{
bool success = false;
Thread.Sleep(5000); // stands for some time consuming Task
success = true;
e.Result = success;
}
private void TestComplete(object sender, RunWorkerCompletedEventArgs e)
{
Console.WriteLine("complete");
}
}
}
我的问题:
我可以向BackgroundWorker
添加多个任务吗?可能不会,因为只有一个RunWorkerAsync。假设每个任务都需要一个worker,我该如何等待这三个任务完成呢?
OK,让我把它改成一个表单应用程序,因为压力不在于在控制台应用程序中运行BackGroundworker,而是我想了解如何为多个BackGroundworker设计应用程序,并等待它们的完成(包括传递和返回参数和结果)。
下面是相同的代码在一个表单应用程序:
using System;
using System.Windows.Forms;
using System.ComponentModel;
using System.Threading;
namespace BackGroundWorkerForm
{
partial class Form1
{
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.IContainer components = null;
private Button button;
/// <summary>
/// Clean up any resources being used.
/// </summary>
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}
#region Windows Form Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent()
{
this.components = new System.ComponentModel.Container();
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.Text = "Form1";
button = new System.Windows.Forms.Button();
button.Location = new System.Drawing.Point(100, 10);
button.Name = "button";
button.Size = new System.Drawing.Size(100, 20);
button.TabIndex = 5;
button.Text = "TestConnection";
button.Click += new System.EventHandler(this.RunTest);
button.Visible = true;
Controls.Add(button);
}
#endregion
private void RunTest(object o, EventArgs e)
{
BackgroundWorker worker = new System.ComponentModel.BackgroundWorker();
worker.RunWorkerCompleted += TestComplete;
worker.DoWork += TestConnection1;
// worker.DoWork += TestConnection2;
// worker.DoWork += TestConnection3;
worker.RunWorkerAsync();
}
private void TestConnection1(object sender, DoWorkEventArgs e)
{
bool success = false;
Thread.Sleep(10000); // stands for some time consuming Task
success = true;
e.Result = success;
}
private void TestConnection2(object sender, DoWorkEventArgs e)
{
bool success = false;
Thread.Sleep(10000); // stands for some time consuming Task
success = true;
e.Result = success;
}
private void TestConnection3(object sender, DoWorkEventArgs e)
{
bool success = false;
Thread.Sleep(10000); // stands for some time consuming Task
success = true;
e.Result = success;
}
private void TestComplete(object sender, RunWorkerCompletedEventArgs e)
{
button.Text= "complete";
}
}
}
下面是一个使用Tasks而不是后台worker的例子。每个Task都是异步启动的,然后在处理结果之前等待所有Task是否成功完成。
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace BGWorkerConsoleApp
{
class Program
{
static Stopwatch sw = new Stopwatch();
static void Main(string[] args)
{
// parse your args
int[] parsedargs = { 1400, 1000, 500 };
int count = 0; // to provide task count
List<Task> tasks = new List<Task>();
sw.Start(); //stopwatch for some to verify the time
foreach (int i in parsedargs)
{
// start a task for each
tasks.Add(Task.Factory.StartNew<bool>(
() => { return myTask(i, String.Format("Task{0} done. ", ++count)); } ) );
}
// wait for all the tasks to complete
Task.WaitAll(tasks.ToArray());
// check the response of each
bool faulted = false;
foreach (Task<bool> t in tasks)
{
if (t.Result == false)
{
faulted = true;
break; //there was a problem so quit looking
}
}
//output some text
if (faulted)
{
Console.WriteLine("There was a problem.");
}
else
Console.WriteLine("complete");
// pause so we can see our output in the debugger
Console.ReadKey();
}
static bool myTask(int time, string msg)
{
Thread.Sleep(time);
if (time == 1000)
return false;
Console.WriteLine(msg + printStopWatchTime());
return true;
}
static string printStopWatchTime()
{
TimeSpan ts = sw.Elapsed;
string elapsedTime = String.Format("{0:00}:{1:00}:{2:00}.{3:00}",
ts.Hours, ts.Minutes, ts.Seconds,
ts.Milliseconds / 10);
return string.Format("RunTime {0}", elapsedTime);
}
}
}
对于您的第一个问题:您可以重用BackgroundWorker
,只要您不尝试运行任务,而它已经在运行(即:IsBusy
必须等于false
)。根据MSDN的说法,如果你试图这样做,它会用InvalidOperationException
狠狠地咬你一口。
关于第二件事:你需要某种同步机制来完成。例如,检查waithhandle . waitall(…)和Barrier类。有很多选择,不只是这些。你可以使用监视器、信号量、互斥锁等等。一定要探索System.Threading
命名空间。
如果你想在后台执行一个特定的任务,BackgroundWorker
是很好的。也就是说,为DoWork
事件分配了一个执行任务并完成任务的处理程序。当然,你可以通过删除旧的事件方法并分配一个新的事件方法来改变事件方法,但重要的部分是一次只能执行一个任务(你不能让同一个后台worker在同一时间运行多次)。
如果你想同时执行两个或更多的任务,你要么需要更多的后台工作者,要么你开始查看任务并行库,正如评论中建议的那样。即使是普通的Thread
也可以。
在任何一种情况下,正如Leandro在他的回答中所说,你想要实现的是使用一些障碍,否则第一个完成任务将在其他任务完成之前启用按钮。但是,您希望等待直到所有任务完成。
我想补充一点,在我看来,BackgroundWorker
的用例是相当有限的。虽然它在某些情况下很方便,但在大多数情况下它不能提供所需的灵活性。