如何正确检测另一个线程中的多个线程何时完全完成?

时间:2015-10-30 16:57:59

标签: c# .net multithreading winforms backgroundworker

我正在运行几个用于执行查询的BackgroundWorker个线程,以便在另一个BackgroundWorker线程中检索DataSet。让我们调用运行这些多个线程的线程“主机线程”'和其他人查询线程'。我想要做的是通过利用主机线程的RunWorkerCompleted事件来判断所有查询线程何时完成填充其DataSet。此事件处理程序的第一行是

while (dataSets.Count < count) { Thread.Sleep(100); } //dataSets is a Dictionary<string, DataSet>

其中count是预期返回的DataSet的总量。我的问题似乎是在填充所有数据集之前,dataSets.Count似乎变为==到count

这是我的完整代码(删除了无用/敏感信息)

        var hostThread = new BackgroundWorker();
        hostThread.RunWorkerCompleted += new RunWorkerCompletedEventHandler(queryWorker_RunWorkerCompleted);
        hostThread.DoWork += (send, even) =>
            {
                foreach (var cs in _connectionStrings)
                {
                    var queryThread = new BackgroundWorker();
                    queryThread.DoWork += (se, eve) =>
                    {
                        var set = DataHandlers.TryGetDataSet(_query, cs, domain, username, pass);
                        dataSets.Add(((DataRow)set.Tables[0].Rows[0]).ItemArray[0].ToString(), set);
                    };
                    queryThread.RunWorkerAsync();
                }
            };
        hostThread.RunWorkerAsync();

RunWorkerCompleted:

 var bw = new BackgroundWorker();
        bw.DoWork += (s, ev) =>
            {
                //Waiting for all DataSets to get populated
                while (dataSets.Count < count) { Thread.Sleep(100); }
                //Thread.Sleep(5000); If I add this, everything works fine, but when I start running more queries in each query thread this needs to be increased.
                this.Invoke((MethodInvoker)delegate()
                {
                    this.Cursor = Cursors.Default;
                    this.Hide();
                    foreach (var set in dataSets)
                    {
                            if (set == null)
                                break;
                            //THIS BLOCK IS NEVER HIT IF I LEAVE OUT THE FIVE SECOND SLEEP
                            var workflowList = new List<string>();
                            foreach (var row in set.Value.Tables[0].Rows)
                            {
                                workflowList.Add(((DataRow)row).ItemArray[_licensed ? 1 : 0].ToString());
                            }
                            ((MainForm)this.OwnedForms[0]).ClientWorkflows = new KeyValuePair<string, List<string>>(set.Key, workflowList);
                    }
                    //This gets hit before setting properties on a child form because it still thinks there are no DataSets in the dataSets dictionary
                    ((MainForm)this.OwnedForms[0]).ShowDialog();
                    this.Close();
                });

            };
        bw.RunWorkerAsync();

正如我在代码中的注释中所述 - 我知道只要在while循环之后添加足够长的睡眠,DataSet就会有效。那么,在主机线程完成事件处理程序中所有查询线程实际完成时,最好的方法是什么?

编辑:每个@ndd这是我最终使用的。

  var queryTasks = new List<Task>();

        var parentTask = Task.Factory.StartNew(() =>
            {
                foreach (var cs in appConfigStrings)
                {
                    queryTasks.Add(Task.Factory.StartNew(() => GetDataSets(mainForm, cs.Key, cs.Value)));
                }
                var array = queryTasks.ToArray();
                Task.WaitAll(array);
            });

        parentTask.ContinueWith((t) =>
            {
                this.Invoke((MethodInvoker)delegate()
               {
                   this.Cursor = Cursors.Default;
                   this.Hide();
                   foreach (var set in dataSets)
                   {
                       var workflowList = new List<string>();
                       foreach (var row in set.Value.Tables[0].Rows)
                       {
                           workflowList.Add(((DataRow)row).ItemArray[_licensed ? 1 : 0].ToString());
                       }
                       ((MainForm)this.OwnedForms[0]).ClientWorkflows = new KeyValuePair<string, List<string>>(set.Key, workflowList);
                   }
                   ((MainForm)this.OwnedForms[0]).ShowDialog();
                   this.Close();
               });
            });

2 个答案:

答案 0 :(得分:2)

就我个人而言,我从不喜欢睡眠,因为它无法预测。如果我必须使用BackgroundWorker,那么我可能会使用IsBusy属性来确定BackgroundThread是否已完成。

使用TPL的示例代码,请注意这只是一个示例,在现实世界中,您可能希望处理异常,传递取消令牌和其他内容:)

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace BGToTPL
{
    class Program
    {
        static void Main(string[] args)
        {
            Task[] tasks = new Task[20];
            //Parent task is starting 20 child tasks
            var parentTask = Task.Run(() =>
            {
                Console.WriteLine("Parent threadid: " + System.Threading.Thread.CurrentThread.ManagedThreadId);
                for (int i = 0; i < 20; i++)
                {
                    tasks[i] = Task.Factory.StartNew(() =>
                    {
                        Console.WriteLine("Child threadid: " + System.Threading.Thread.CurrentThread.ManagedThreadId);                      
                        Task.Delay(15000);
                    });
                }
            });

            parentTask.Wait();

            Console.WriteLine("Parent task has started creating and running all the child tasks, now waiting for child tasks to be over.");

            //Now wait for all the tasks to be done
            Task.WaitAll(tasks);

            Console.WriteLine("All the tasks are done");
            Console.ReadKey();
        }
    }
}

输出

Output

答案 1 :(得分:1)

这样的事情怎么样? - 当然,如果没有选择TPL:

  private readonly IList<BackgroundWorker> workers = new List<BackgroundWorker>();

  private void Run()
  {
     var worker1 = new BackgroundWorker();
     worker1.DoWork += (sender, args) => Thread.Sleep(1000);
     worker1.RunWorkerCompleted += (sender, args) => this.CheckThreads();

     var worker2 = new BackgroundWorker();
     worker2.DoWork += (sender, args) => Thread.Sleep(1000);
     worker2.RunWorkerCompleted += (sender, args) => this.CheckThreads();

     lock (this.workers)
     {
        this.workers.Add(worker1);
        this.workers.Add(worker2);
     }

     worker1.RunWorkerAsync();
     worker2.RunWorkerAsync();
  }

  private void CheckThreads()
  {
     lock (this.workers)
     {
        if (this.workers.All(w => !w.IsBusy))
        {
           Console.WriteLine("All workers completed");
        }
     }
  }