推出成千上万的Backgroundworkers的正确方法?

时间:2016-01-05 08:05:26

标签: c#

我写了一个程序,扫描网络中的10.000台计算机的正常运行时间,活动用户,空闲时间和ping。 该计划表现很好。主要是在大约3分钟内检查所有计算机。 但最近,该计划一直在冻结。它完成了所有任务,但不会退出。 我像这样启动所有BackGroundWorkers:

foreach (DataRow computer in computerTable.Rows)
                {
                    //Added check to see if computer is in inputlist for inputfilemode
                    if (!inputFileMode || (inputFileMode && computerList.Contains(computer["ComputerName"].ToString())))
                    {
                        BackgroundWorker bw = new BackgroundWorker();
                        //Determine background jobs
                        bw.DoWork += bw_DoWork;
                        bw.RunWorkerCompleted += bw_RunWorkerCompleted;

                        bw.RunWorkerAsync(computer); //Start background worker
                        numberOfWorkers++;
                    }
                }


                int lastNr = 0;
                int skipHangingJobsCounter = 0;
                do
                {
                    lastNr = numberOfWorkers;
                    Thread.Sleep(1000);

                    Console.WriteLine("Tijd: " + DateTime.Now.ToString("yyyyMMdd-HH:mm:ss") + " #Jobs queued: " + numberOfWorkers.ToString() + " Jobs/sec: " + (lastNr - numberOfWorkers).ToString() + " -ForcedShutdowns: " + forcedReboots.ToString() + " -MSIEXEC Exceptions: " + machinesMSI.ToString()); //Report progress
                    if (lastNr - numberOfWorkers == 0)
                    {
                        skipHangingJobsCounter++;
                        if (skipHangingJobsCounter > 30) break; //Safety breakout if a worker hangs
                    }
                    else
                    {
                        skipHangingJobsCounter = 0;
                    }

                    //Timing break
                    double minutesRunning = DateTime.Now.Subtract(startOfJob).TotalMinutes;
                    if (minutesRunning > 13)
                    {
                        Console.WriteLine("Job is running for 14 minutes. Job will be cancelled. Jobs queued remaining: " + numberOfWorkers.ToString());
                        break;
                    }

                } while (numberOfWorkers > 0); //Wait till all jobs are finished

我想知道:这是启动和跟踪所有后台工作的最有效方式吗?最好是创建一个List数组吗? 我认为这会占用大量内存吗?

在do_work部分,我有很多WMI查询,正常运行时间等等。由于挂起WMI查询,我的程序是否会退出?有人试过这个吗?

所以我基本上有两个问题:

1-我是否以有效/正确的方式启动了背景工作者?

2-线程中的WMI调用是否可能导致程序无法退出?

更新:

我重新编写了如下代码。它现在快了大约3倍。我仍然使用1个后台工作者,但仅限于,所以我可以在主线程上给出反馈。它的工作方式我认为:)

主线程:

                    BackgroundWorker bw = new BackgroundWorker();
                bw.DoWork += bw_DoWork;
                bw.RunWorkerCompleted +=bw_RunWorkerCompleted;
                bw.RunWorkerAsync();

                int lastNr = numberOfWorkers;
                do
                {
                    lastNr = numberOfWorkers;
                    Thread.Sleep(1000); //Give feedback every second

                    Console.WriteLine("Tijd: " + DateTime.Now.ToString("yyyyMMdd-HH:mm:ss") + " #Jobs queued: " + numberOfWorkers.ToString() + " Jobs/sec: " + (lastNr - numberOfWorkers).ToString() + " -ForcedShutdowns: " + forcedReboots.ToString() + " -MSIEXEC Exceptions: " + machinesMSI.ToString()); //Report progress

                    //Timing break
                    double minutesRunning = DateTime.Now.Subtract(startOfJob).TotalMinutes;
                    if (numberOfWorkers < 3)
                    {
                        //We are waiting for 3 last workers. Give feedback which 3 thse are
                        foreach (Task t in taskArray)
                        {
                            if (t.Status == TaskStatus.Running)
                            {
                                Console.WriteLine("Waiting for " + taskList[t.Id]);
                            }
                        }
                    }
                    if (minutesRunning > 13)
                    {
                        Console.WriteLine("Job is running for 14 minutes. Job will be cancelled. Jobs queued remaining: " + numberOfWorkers.ToString());
                        break;
                    }

                } while (!jobDone); //Wait till all jobs are finished

背景工作

        static void bw_DoWork(object sender, DoWorkEventArgs e)
    {
        JobLauncher();
    }

    static void JobLauncher()
    {


        foreach (DataRow computer in computerTable.Rows)
        {
            //Added check to see if computer is in inputlist for inputfilemode
            if (!inputFileMode || (inputFileMode && computerList.Contains(computer["ComputerName"].ToString())))
            {
                Task t = Task.Factory.StartNew(() => CheckComputer(computer));
                taskList.Add(t.Id, computer["ComputerName"].ToString());
                taskArray.Add(t);

                numberOfWorkers++;
            }
        }

        Task.WaitAll(taskArray.ToArray());
    }

1 个答案:

答案 0 :(得分:5)

而不是所有这些,您可以更好地使用预定义的方法来检查所有任务是否已经结束。您的代码如果非常容易出错并且可能会关闭整个应用程序。为什么为每台计算机启动1个线程? 10.000个主题?这对我来说似乎有点过分了。

我的建议是使用任务而不是后台工作人员。如果您有网络I / O,则最有可能await,从而提高整体性能。只需Task.WhenAll他们,你就会注意到他们准备好了。

另一种方法是使用Parrallel库。您可以Parrallel.Foreach计算机列表,将它们调整到最多16个左右,.NET框架将完成所有循环并检查您。