我有一个使用BackGroundWorker执行一组任务的c#应用程序:
private void buttonStartCheckOut_Click(object sender, EventArgs e)
{
BackgroundWorker checkOuter = new BackgroundWorker();
checkOuter.DoWork += new DoWorkEventHandler(checkOuter_DoWork);
checkOuter.RunWorkerAsync();
checkOuter.RunWorkerCompleted += new RunWorkerCompletedEventHandler(checkOuter_RunWorkerCompleted);
}
void checkOuter_DoWork(object sender, DoWorkEventArgs e)
{
if (textBoxCICheckOut.Text != "")
CheckOutCI();
if (textBoxCACheckOut.Text != "")
CheckOutCA();
if (textBoxCAuthCheckOut.Text != "")
CheckOutCAuth();
if (textBoxCLCheckOut.Text != "")
CheckOutCL();
if (textBoxCCCheckOut.Text != "")
CheckOutCC();
}
正如你所看到的,我只有2个线程;一个用于GUI,一个用于次要任务 当我完成所有功能时,我很容易跟踪 现在我想通过为CheckOutCI(),CheckOutCA()和其他人创建一个单独的线程来使它更快。创建5个后台工作者看起来有点脏。
我想问:
如何跟踪所有功能何时完成执行。
如果任何一个函数返回异常,我想将其显示给用户并要求用户更正用户并再试一次。我希望我能够正确解释我的问题 请根据我对帖子的评论,按照wdavo编辑代码。
答案 0 :(得分:4)
我会看一下使用Task库(假设您运行的是.NET 4.5或更高版本)。在大多数情况下,我发现使用它比后台工作者好得多。
(注意你仍然可以在.NET 4中使用Task库,但是Task.WhenAll仅在4.5中可用)
http://msdn.microsoft.com/en-us/library/dd235618
如果不重写整个程序,这里有一个如何使用它的例子:
将简单条件逻辑移动到按钮
private void button1_Click(object sender, EventArgs e)
{
var tasks = new List<Task>();
if (Text == "A")
{
tasks.Add(funcA());
}
if (Text == "B")
{
tasks.Add(funcB());
}
//And so on....
Task.WhenAll(tasks.ToArray()).ContinueWith(t =>
{
if (t.Exception != null)
{
//One of the tasks threw an exception
MessageBox.Show("There was an exception!");
}
else
{
//None of the tasks threw an exception
MessageBox.Show("No Exceptions!");
}
});
}
我们将任务添加到集合中,以便我们可以通过Task.WhenAll知道它们何时完成。当集合中的所有任务完成后,将显示一个消息框。如果集合中的任何任务抛出异常,则将填充“t”的Exception属性。特定异常作为此异常的内部异常存在。
将线程代码移动到单个任务/功能。您将创建结帐功能看起来与此类似:
private Task funcA()
{
return Task.Factory.StartNew(() =>
{
try
{
//Code running here will be executed on another thread
//This is where you would put your time consuming work
//
//
}
catch(Exception ex)
{
//Handle any exception locally if needed
//If you do handle it locally, make sure you throw it again so we can see it in Task.WhenAll
throw ex;
}
//Do any required UI updates after the work
//We aren't on the UI thread, so you will need to use BeginInvoke
//'this' would be a reference to your form
this.BeginInvoke(new Action(() =>
{
//...
}));
});
}
这是做什么的以下
答案 1 :(得分:1)
启动比CPU或内核更多的线程实际上可以使您的应用程序变慢。当存在比CPU更多的CPU绑定线程时,操作系统需要在线程之间更频繁地进行上下文切换 - 这非常昂贵,并且可能导致操作系统在线程之间花费更多时间上下文切换,而不是让他们有时间工作。 p>
您可以使用并行任务库的并行方面自动在CPU之间分配负载。例如:
Action[] actions = new Action[] {CheckOutCI, CheckOutCA, CheckOutCAuth, CheckOutCL, CheckOutCC};
Parallel.ForEach(actions, e=>e());
......这不是你想要的;但应该给你一个大致的想法。例如根据当前条件填充actions
。
答案 2 :(得分:0)
您需要在backgroundworker中使用ReportProgress方法
void checkOuter_DoWork(object sender, DoWorkEventArgs e)
{
if (textBoxCICheckOut.Text != "")
CheckOutCI();
checkOuter.ReportProgress(completionPercentage,"Error message");
可以在checkOuter_ProgressChanged事件中捕获ReportProgress中发送的数据
checkOuter_ProgressChanged(object sender,ProgressChangedEventArgs e)
{
int percentage = e.ProgressPercentage;
string message = e.UserState;
}