简体   繁体   English

多个后台线程

[英]Multiple background threads

I have one c# application that uses BackGroundWorker to do a group of tasks: 我有一个使用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();
        }

As you can see, I have only 2 threads; 如您所见,我只有2个线程; one for GUI and one for secondary task. 一个用于GUI,另一个用于辅助任务。
Its easy for me to track when all the functions finish. 当所有功能完成时,我很容易跟踪。
Now I want to make it more fast by creating a separate thread for CheckOutCI(), CheckOutCA() and others.Creating 5 background workers looks kinda dirty. 现在我想通过为CheckOutCI(),CheckOutCA()和其他线程创建一个单独的线程来使其更快。创建5个后台工作器看起来有点脏。

I want to ask: 我想问问:
How will I keep track of when all the functions have finished executing. 我将如何跟踪所有功能何时完成执行。

If any one function returned an exception, I want to display it to user and ask the user to correct the user and try again. 如果任何一个函数返回了异常,我想将其显示给用户,并请用户纠正该用户,然后重试。 I hope I am able to explain my question properly. 我希望我能够正确解释我的问题。
PLEASE edit the code by wdavo as per my comment on his post. 请按照我对他的帖子的评论,用wdavo编辑代码。

I'd look at using the Task library (Assuming you are running .NET 4.5 or later). 我将研究使用Task库(假设您正在运行.NET 4.5或更高版本)。 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) (请注意,您仍然可以在.NET 4中使用Task库,但是Task.WhenAll仅在4.5中可用)

http://msdn.microsoft.com/en-us/library/dd235618 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. 我们将任务添加到集合中,以便通过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. 如果集合中的任何任务引发了异常,则将填充“ t”的Exception属性。 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 如果有异常,则在本地处理。重新抛出该异常,以便在执行“ Task.WhenAll”时可以知道任务已失败
  • Updates the UI after the work is done. 工作完成后更新UI。 You need to call BeginInvoke to run the code on the UI thread to avoid cross threading issues. 您需要调用BeginInvoke在UI线程上运行代码,以避免跨线程问题。

Starting up more threads than CPUs or cores can actually make your application slower. 启动比CPU或内核更多的线程实际上会使您的应用程序变慢。 When there are more CPU-bound threads than CPUs the OS needs to context switch more often between the threads--which is hugely expensive and could result in the OS spending more time context switching between your threads than giving them time to work. 当与CPU绑定的线程数量超过CPU数量时,操作系统需要在线程之间进行上下文切换的频率更高-这非常昂贵,并且可能导致操作系统在线程之间切换上下文所花费的时间多于给它们的工作时间。

You can use the parallel aspect of the Parallel Task Library to automatically distribute your load across CPUs. 您可以使用并行任务库的并行方面来自动在CPU之间分配负载。 For example: 例如:

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

...which isn't exactly what you want; ...这不是您想要的; but should give you a general idea. 但应该给您一个大致的想法。 eg populate actions based on current conditions. 例如根据当前条件填充actions

You need to use ReportProgress method in backgroundworker 您需要在backgroundworker中使用ReportProgress方法

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

The data sent in ReportProgress can be captured in checkOuter_ProgressChanged event 可以在checkOuter_ProgressChanged事件中捕获ReportProgress中发送的数据

 checkOuter_ProgressChanged(object sender,ProgressChangedEventArgs e)
 {
     int percentage = e.ProgressPercentage;
     string message = e.UserState;
 }

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM