1

我有一个使用 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 编辑代码。

4

3 回答 3

4

I'd look at using the Task library (Assuming you are running .NET 4.5 or later). I find it much better to use than background workers in most cases.

(Note you can still use the Task library in .NET 4, however Task.WhenAll is only available in 4.5)

http://msdn.microsoft.com/en-us/library/dd235618

Without rewriting your whole program, here's an example of how you would use it:

Move your simple conditional logic to the button

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!");
    }
  });

}

We add the tasks to a collection so we can know when they all finish via Task.WhenAll. When all the tasks in the collection have finished, a message box will be displayed. If any of the tasks in the collection have thrown an exception, the Exception property of 't' will be populated. The specific exceptions exist as inner exceptions of this exception.

Move your threading code to individual task/functions. You'd create your checkout functions to look similar to this:

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(() =>
    {
      //...
    }));

  });
}

What this does is the following

  • Creates a and runs a task which does some work on a thread from the thread pool
  • If there is an exception, we handle it locally .We re-throw the exception so that we can know that a task has failed when 'Task.WhenAll' is executed
  • Updates the UI after the work is done. You need to call BeginInvoke to run the code on the UI thread to avoid cross threading issues.
于 2012-09-04T10:57:13.353 回答
1

启动比 CPU 或内核更多的线程实际上会使您的应用程序变慢。当 CPU 绑定线程多于 CPU 时,操作系统需要更频繁地在线程之间进行上下文切换——这非常昂贵,并且可能导致操作系统花费更多时间在线程之间进行上下文切换,而不是让它们有时间工作。

您可以使用并行任务库的并行方面来自动跨 CPU 分配负载。例如:

Action[] actions = new Action[] {CheckOutCI, CheckOutCA, CheckOutCAuth, CheckOutCL, CheckOutCC};
Parallel.ForEach(actions, e=>e());

...这不是您想要的;但应该给你一个大致的想法。actions例如,根据当前条件填充。

于 2012-09-04T15:05:34.993 回答
0

您需要在后台工作人员中使用 ReportProgress 方法

void checkOuter_DoWork(object sender, DoWorkEventArgs e)
    {
        if (textBoxCICheckOut.Text != "")
            CheckOutCI();
            checkOuter.ReportProgress(completionPercentage,"Error message");

ReportProgress 中发送的数据可以在 checkOuter_ProgressChanged 事件中捕获

 checkOuter_ProgressChanged(object sender,ProgressChangedEventArgs e)
 {
     int percentage = e.ProgressPercentage;
     string message = e.UserState;
 }
于 2012-09-04T10:16:46.403 回答