C#:多线程(WMI)应用程序锁定GUI

本文关键字:应用程序 锁定 GUI WMI 多线程 | 更新日期: 2023-09-27 18:24:17

我想知道是否有人对我在下面的代码中遇到的问题有任何指导。代码的目的是:

  • 加载1000台计算机的列表
  • 使用WMI和Parallel.foreach()从它们收集信息
    • 将此信息写入磁盘
  • 使用每台计算机的状态更新主GUI中的列表视图

我遇到的问题是在连接到大约1000台计算机后,主GUI应用程序锁定。为了避免明显的GUI锁定,我一直在使用Invoke()来更新主GUI。

这个错误很难在测试环境中重现,因为它是在连接到这么多计算机后发生的。我对这个bug的看法是,它可能是以下其中之一(我不是专家):

  • 我在多线程/Parallel.foreach()中犯了一个新手错误
  • 调用Invoke()的次数太多,主GUI一直忙于更新
  • 我已经为这个程序使用了所有可用的资源(机器似乎有更多的RAM可用)
  • 在远程计算机上完成的处理太慢(WAN链接)-为什么这会影响主GUI

以下是代码,它以ScanButton_Click()开头:

namespace WMIScan
{
    public partial class MainForm : Form
    {
        public MainForm()
        {
            InitializeComponent();
        }
        // ...Other code removed as it is irrelevant
        private void ScanButton_Click(object sender, EventArgs e)
        {
            /*
             * Scan all computers in the list
             */
            List<string> computers = new List<string>();
            foreach (ListViewItem item in CompList1.Items)
            {
                computers.Add(item.Text);
                item.SubItems[1].Text = "";
                item.SubItems[2].Text = "";
                item.SubItems[3].Text = "";
            }
            LaunchScanning(computers);
        }
        private void LaunchScanning(List<string> computers)
        {
            /*
             * This function is responsible for launching a scan against all computers in the list
             * It handles the multithreading of scan jobs.
             */
            toolStripProgressBar1.Minimum = 0;
            toolStripProgressBar1.Value = 0;
            toolStripProgressBar1.Maximum = computers.Count;
            System.Threading.Tasks.Task Processing;
            //Support the scanning of multiple computers.
            toolStripStatusLabel1.Text = "Scanning of " + computers.Count + " computers(s) started at: " + DateTime.Now;
            Processing = Task.Factory.StartNew(() =>
                {
                    Parallel.ForEach(computers, new ParallelOptions { MaxDegreeOfParallelism = max_threads }, computer =>
                    {
                        BeginScanning(computer);
                        toolStripProgressBar1.GetCurrentParent().BeginInvoke(new MethodInvoker(delegate { toolStripProgressBar1.Value++; }));
                    });
                    toolStripStatusLabel1.Text = "Scanning of " + computers.Count + " computers(s) completed.";
                }
            );
            
        }
        private void BeginScanning(string computer)
        {
            /*
             * This function is responsible for conducting the scanning of a single computer
             */
            ManagementScope cimv2_scope = new ManagementScope();
            ConnectionOptions options = new ConnectionOptions();
            ObjectQuery query;
            ManagementObjectSearcher searcher;
            ManagementObjectCollection queryCollection;
            System.IO.StreamWriter file = null;
            string completed_jobs = "";
            string errors = "";
            string[] listview_output = { "", "","" };
            //Check if credentials have been provided.
            if (username != "" && password != "")
            {
                options.Username = username;
                options.Password = password;
            }
            //Attempt inital connection
            cimv2_scope = new ManagementScope(@"''" + computer + @"'root'CIMV2", options);
            try
            {
                //Create new scope connections
                cimv2_scope.Connect();
                //Attempt to open output file.
                try
                {
                    file = new System.IO.StreamWriter(output_dir + @"'" + computer + ".txt");
                    file.WriteLine("######Start " + DateTime.Now);
                    //Query Operating System
                    try
                    {
                        query = new ObjectQuery("SELECT * FROM Win32_OperatingSystem");
                        searcher = new ManagementObjectSearcher(cimv2_scope, query);
                        queryCollection = searcher.Get();
                        foreach (ManagementObject m in queryCollection)
                        {
                            DateTime InstallDate = ManagementDateTimeConverter.ToDateTime(m["InstallDate"].ToString());
                            DateTime LastBootUpTime = ManagementDateTimeConverter.ToDateTime(m["LastBootUpTime"].ToString());
                            DateTime LocalDateTime = ManagementDateTimeConverter.ToDateTime(m["LocalDateTime"].ToString());
                            file.WriteLine("OS," + computer + "," + m["CSName"] + "," + m["BuildNumber"] + "," + m["Caption"]
                                + "," + m["Version"] + "," + m["OSArchitecture"] + "," + m["ServicePackMajorVersion"] + ","
                                + m["ServicePackMinorVersion"] + "," + m["CurrentTimeZone"] + "," + InstallDate + "," +
                                LastBootUpTime + "," + LocalDateTime + "," + m["OSLanguage"] + "," + m["OSProductSuite"] +
                                "," + m["OSType"] + "," + m["RegisteredUser"] + "," + m["SerialNumber"] + "," + m["SystemDevice"]
                                + "," + m["SystemDirectory"] + "," + m["SystemDrive"] + "," + m["WindowsDirectory"]);
                        }
                        completed_jobs = "OS";
                    }
                    catch (Exception e)
                    {
                        errors += ("[Operating System] " + e.Message);
                    }
                    // ... Many more WMI queries here
                   
                    //Processing completed
                    file.WriteLine("Completed " + DateTime.Now);
                    file.Close();
                    CompList1.BeginInvoke(new MethodInvoker(delegate
                    {
                        ListViewItem tmp = CompList1.FindItemWithText(computer);
                        tmp.SubItems[1].Text = "True";
                        tmp.SubItems[2].Text = completed_jobs;
                        tmp.SubItems[3].Text = errors;
                    }));
                }
                catch (Exception e) //File Open Error
                {
                    CompList1.BeginInvoke(new MethodInvoker(delegate
                    {
                        ListViewItem tmp = CompList1.FindItemWithText(computer);
                        tmp.SubItems[1].Text = "Failed";
                        tmp.SubItems[2].Text = "";
                        tmp.SubItems[3].Text = e.Message;
                    }));
                }
            }
            catch (Exception e) //Scope Connection Error
            {
                CompList1.BeginInvoke(new MethodInvoker(delegate
                {
                    ListViewItem tmp = CompList1.FindItemWithText(computer);
                    tmp.SubItems[1].Text = "Failed";
                    tmp.SubItems[2].Text = "";
                    tmp.SubItems[3].Text = e.Message;
                }));
            }
        }
     }
}

我是C#和StackOverflow的新手,如果有任何帮助,我们将不胜感激!

谢谢你,

Joss

C#:多线程(WMI)应用程序锁定GUI

据我所知,Parallel.ForEach根据您拥有的内核数量运行。也就是说,如果你有4个核心,你将能够在4个线程中并行处理。

为什么不做一个foreach并为您希望处理的每个操作创建一个Task呢。(我还没有测试过,但你明白了)

var tasks = new List<Task>();

    foreach(var computer in computers)
    {
      var t =Task.Factory.StartNew(() =>
                    {
                        BeginScanning(computer);
                         Acttion myUiStuff = () =>
                         {
                            toolStripProgressBar1.Value++;
                            toolStripStatusLabel1.Text = "Scanning of " + computers.Count + " computers(s) completed.";
                         };
                         //im assuming you are using WinForms; UI objects must be modified by UI thread so thats is the call to BeginInvoke; If its WPF call the Dispatcher.
                         BeginInvoke(myUiStuff);
                    }
      tasks.Add(t);
    }
    Task.WaitAll(tasks.ToArray());