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

C# C:多线程(WMI)应用程序锁定GUI,c#,multithreading,user-interface,network-programming,wmi,C#,Multithreading,User Interface,Network Programming,Wmi,我想知道是否有人对我在下面的代码中遇到的问题有任何指导。该守则的目的是: 加载1000台计算机的列表 使用WMI和Parallel.foreach从它们收集信息 将此信息写入磁盘 使用每台计算机的状态更新主GUI中的listview 我遇到的问题是,在连接到大约1000台计算机后,主GUI应用程序会锁定。为了避免明显的GUI锁定,我一直在使用Invoke更新主GUI 这种错误很难在测试环境中重现,因为它发生在连接到这么多计算机之后。我对这个bug的看法是,它可能是以下原因之一我不是专家: 我在使

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

加载1000台计算机的列表 使用WMI和Parallel.foreach从它们收集信息 将此信息写入磁盘 使用每台计算机的状态更新主GUI中的listview 我遇到的问题是,在连接到大约1000台计算机后,主GUI应用程序会锁定。为了避免明显的GUI锁定,我一直在使用Invoke更新主GUI

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

我在使用multi-threading/Parallel.foreach时犯了一个新手错误 要调用的调用太多,主GUI一直忙于更新 我已经使用了所有可用的资源,因为程序机器似乎有更多的RAM可用 远程计算机上的处理速度太慢,WAN链接太慢-为什么会影响主GUI? 以下是代码,它以ScanButton\u单击开始: 命名空间WMIScan { 公共部分类主窗体:窗体 { 公共主窗体 { 初始化组件; } //…删除了其他与此无关的代码 私有无效扫描按钮\u单击对象发送者,事件参数e { /* *扫描列表中的所有计算机 */ 列表计算机=新列表; CompList1.Items中的foreach ListViewItem项 { computers.Additem.Text; 项。子项[1]。文本=; 项。子项[2]。文本=; 项。子项[3]。文本=; } 启动扫描计算机; } 专用void启动扫描列表计算机 { /* *此函数负责对列表中的所有计算机启动扫描 *它处理扫描作业的多线程处理。 */ toolStripProgressBar1。最小值=0; toolStripProgressBar1.Value=0; toolStripProgressBar1.Maximum=computers.Count; System.Threading.Tasks.Task处理; //支持多台计算机的扫描。 toolStripStatusLabel1.Text=扫描+台计算机。计数+台计算机开始于:+DateTime.Now; 处理=Task.Factory.StartNew=> { Parallel.ForEachcomputers,新的ParallelOptions{MaxDegreeOfParallelism=max_threads},computer=> { 开始扫描计算机; toolStripProgressBar1.GetCurrentParent.BeginInvokeNewMethodInvokerDelegate{toolStripProgressBar1.Value++;}; }; toolStripStatusLabel1.Text=扫描+台计算机。计数+台计算机已完成。; } ; } 私有无效开始扫描字符串计算机 { /* *此功能负责对一台计算机进行扫描 */ ManagementScope cimv2_scope=新的管理范围; ConnectionOptions选项=新的ConnectionOptions; 对象查询; ManagementObjectSearcher搜索器; ManagementObjectCollection查询集合; System.IO.StreamWriter文件=null; 字符串已完成_作业=; 字符串错误=; 字符串[]listview_输出={,}; //检查是否提供了凭据。 如果用户名!=&密码!= { 选项。用户名=用户名; 选项。密码=密码; } //尝试初始连接 cimv2_scope=new ManagementScope@\\+computer+@\root\cimv2,选项; 尝试 { //创建新的作用域连接 cimv2_scope.Connect; //尝试打开输出文件。 尝试 { file=new System.IO.StreamWriteroutput_dir+@\+computer+.txt; file.WriteLineStart+DateTime.Now; //查询操作系统 尝试 { query=新对象queryselect*自Win32\u操作系统; searcher=新管理对象searchercimv2\u范围,查询; queryCollection=searcher.Get; queryCollection中的foreach ManagementObject m { DateTime InstallDate=ManagementDateTimeConverter.ToDateTimem[InstallDate].ToString; DateTime LastBootUpTime=ManagementDateTimeConverter.ToDateTimem[LastBootUpTime].ToString; DateTime LocalDateTime=ManagementDateTimeConverter.ToDateTimem[LocalDateTime].ToString; file.WriteLineOS、+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]; } 完成的作业=操作系统; } 捕获异常e { 错误+=[操作系统]+e.消息; } // ... 这里有更多的WMI查询 //处理完成 file.WriteLineCompleted+DateTime.Now; 文件。关闭; CompList1.beginInvokeNewMethodInvokerDelegate { ListViewItem tmp=CompList1.FindItemWithTextcomputer; tmp.子项[1]。Text=True; tmp.子项[2]。Text=已完成的作业; tmp.子项[3]。Text=错误; }; } 捕获异常e//文件打开错误 { CompList1.beginInvokeNewMethodInvokerDelegate { ListViewItem tmp=CompList1.FindItemWithTextcomputer; tmp.SubItems[1]。Text=失败; tmp.子项[2].文本=; tmp.子项[3],Text=e.消息; }; } } 捕获异常e//Scope连接错误 { CompList1.beginInvokeNewMethodInvokerDelegate { ListViewItem tmp=CompList1.FindItemWithTextcomputer; tmp.SubItems[1]。Text=失败; tmp.子项[2].文本=; tmp.子项[3],Text=e.消息; }; } } }
} 据我所知,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());

当然,用户界面线程的更新速度大约是肉眼所能看到的50倍。当然,这对你的眼睛来说是没有用的,对完全被淹没并燃烧着100%核心的UI线程来说也是没有用的。收集列表中的结果,偶尔更新一次UI。有趣的是,如果这确实是一个问题,我想我应该有一个线程负责从Parallel.Foreach任务收集信息,然后定期更新GUI?有关于排队的建议吗?你没有解释为什么这样可以解决OP的问题。简单地重写代码没有明显的原因不是一个很好的答案,是吗?是的,这是WinForms。BeginScan只能扫描一台计算机,这就是为什么我必须添加parralel.ForEach来在每台独立的计算机上工作;Task.Factory.StartNew确保GUI线程不负责所有这些活动,以避免锁定。谢谢你提供的信息,我不知道你可以像那样使用BeginInvoke。@jalf没有评论我的评论,你为什么不至少回复一个答案呢。简单地评论答案并没有多大帮助,是吗?@Alex Peta:感谢你提出的答案。@Joss干杯,我在工作,没有环境设置等。只是写这个,没有测试。