C#: многопоточное (WMI) приложение блокирует графический интерфейс

Мне было интересно, есть ли у кого-нибудь руководство по проблеме, с которой я столкнулся, с кодом ниже. Целью кодов является:

  • Загрузить список 1000 компьютеров
  • Соберите информацию от них, используя WMI и Parallel.foreach()
    • Запишите эту информацию на диск
  • Обновите представление списка в основном графическом интерфейсе с каждым состоянием компьютеров

Проблема, с которой я столкнулся, заключается в том, что после подключения к 1000 компьютерам основное приложение с графическим интерфейсом блокируется. Чтобы избежать очевидных блокировок графического интерфейса, я использовал Invoke() для обновления основного графического интерфейса.

Эту ошибку сложно воспроизвести в тестовой среде, как это происходит после подключения к стольким компьютерам. Я думаю об ошибке, что это может быть одним из следующих (я не эксперт):

  • Я сделал ошибку новичка с многопоточностью /Parallel.foreach()
  • Invoke() просто слишком много обращений, и основной графический интерфейс все время занят обновлением
  • Я использовал все доступные ресурсы для программы (кажется, что на машине больше оперативной памяти)
  • Обработка, выполняемая на удаленных компьютерах, выполняется слишком медленно (по каналам глобальной сети). Почему это влияет на основной графический интерфейс?

Вот код, он начинается с 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# и Stackru, любая помощь будет принята с благодарностью!

Спасибо,

амулет

1 ответ

Насколько я знаю, Parallel.ForEach работает против того, сколько у вас ядер. то есть. если у вас 4 ядра, вы сможете параллельно обрабатывать 4 потока.

Почему бы вам не выполнить foreach и не создать задачу для каждой операции, которую вы хотите обработать. (Я не проверял это, но вы поняли идею)

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());
Другие вопросы по тегам