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
据我所知,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());