C# 多个后台线程

C# 多个后台线程,c#,winforms,multithreading,backgroundworker,C#,Winforms,Multithreading,Backgroundworker,我有一个c#应用程序,它使用BackGroundWorker执行一组任务: private void buttonStartCheckOut_Click(object sender, EventArgs e) { BackgroundWorker checkOuter = new BackgroundWorker(); checkOuter.DoWork += new DoWorkEventHandler(chec

我有一个c#应用程序,它使用BackGroundWorker执行一组任务:

 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();
        }
如你所见,我只有两条线;一个用于GUI,一个用于辅助任务。
我很容易跟踪所有函数何时完成。
现在我想通过为CheckOutCI()、CheckOutCA()和其他线程创建一个单独的线程来提高速度。创建5个后台工作线程看起来有点脏

我想问:
我将如何跟踪所有函数何时完成执行

如果任何一个函数返回异常,我想将其显示给用户,并要求用户更正该用户,然后重试。我希望我能正确地解释我的问题。

请根据我在wdavo帖子上的评论编辑代码。

您需要在backgroundworker中使用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;
 }

您需要在backgroundworker中使用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;
 }

我将考虑使用任务库(假设您运行的是.NET4.5或更高版本)。我发现在大多数情况下,使用它比使用后台工作人员要好得多

(注意,您仍然可以使用.NET 4中的任务库,但是Task.Wheall仅在4.5中可用)

在不重写整个程序的情况下,下面是一个如何使用它的示例:

将简单的条件逻辑移动到按钮

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”时知道任务失败
  • 工作完成后更新UI。您需要调用BeginInvoke在UI线程上运行代码,以避免交叉线程问题

我会考虑使用任务库(假设您运行的是.NET 4.5或更高版本)。我发现在大多数情况下,使用它比使用后台工作人员要好得多

(注意,您仍然可以使用.NET 4中的任务库,但是Task.Wheall仅在4.5中可用)

在不重写整个程序的情况下,下面是一个如何使用它的示例:

将简单的条件逻辑移动到按钮

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”时知道任务失败
  • 工作完成后更新UI。您需要调用BeginInvoke在UI线程上运行代码,以避免交叉线程问题

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

您可以使用并行任务库的并行特性在CPU之间自动分配负载。例如:

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

…这并不是你想要的;但是应该给你一个大概的想法。e、 g.根据当前条件填充
操作。

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

您可以使用并行任务库的并行特性在CPU之间自动分配负载。例如:

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

…这并不是你想要的;但是应该给你一个大概的想法。e、 g.根据当前情况填充
操作

听起来你可能会遇到一些复杂的问题。您可以使用5名后台工作人员,但管理哪些完成了,哪些失败了可能会很困难。您可以尝试查看此问题。您有跨线程问题。后台工作人员不在UI线程上,无法访问UI控件(如textBoxCCCheckOut)。@RichardSchneider是的,我知道,我正在正确地将它们分配到UI线程。听起来你可能会遇到一些复杂的问题。您可以使用5名后台工作人员,但管理哪些完成了,哪些失败了可能会很困难。您可以尝试查看此问题。您有跨线程问题。后台工作人员不在UI线程上,无法访问UI控件(如textBoxCCCheckOut)。@RichardSchneider是的,我知道这一点,并且我正确地将其分配到UI线程。非常好,简洁的解释。当我需要同时执行多个后台操作时,我自己也会使用这种方法。@davo:一旦任何任务抛出异常,我将如何中止其他任务,以及如何检查所有任务是否已无异常完成?我是否应该声明一些bool变量,每个任务在完成时将其设置为true,然后在Task.whall中检查所有bool?这会起作用,但实际上不是一个好的做法。“任务”集合中的任务具有一个异常属性,您可以检查该属性以查看任务是否导致了异常。我想说,如果你需要在另一个任务失败时取消一个任务,那么你尝试做的事情就有根本性的错误(当你试图取消它时,你不知道任务在哪里,所以你可能试图在它已经完成时取消它)。异常:取消:(如果为了避免等待,可以取消任务)因此任务中的异常会自动处理,我应该检查任务中的迭代以查看是否发生异常?如果出现异常,异常是否为空