C#后台工作者-我应该同时使用几个?

问题描述 投票:0回答:1

我正在用C#编写MVVM(Caliburn.Micro)应用程序,该应用程序使用PowerShell在远程计算机上运行WMI查询。这些计算机是从选定的Active Directory OU加载的,因此可以有任意数量的计算机。 WMI查询的结果将显示在UI上,我想同时运行多个查询,并在查询完成后立即显示每个查询。我正在使用多个后台工作人员来实现这一目标,并且目前正在运行。但是,我当前的代码将为OU中的每台计算机创建一个后台工作器,而没有任何形式的队列或限制。

private void QueryComputers()
{
    foreach (RemoteComputer computer in Computers)
    {
        BackgroundWorker bw = new BackgroundWorker();
        bw.WorkerReportsProgress = true;
        bw.DoWork += BackgroundWorker_DoWork;
        bw.ProgressChanged += BackgroundWorker_ProgressChanged;
        bw.RunWorkerCompleted += BackgroundWorker_RunWorkerCompleted;
        bw.RunWorkerAsync(computer.DNSHostName);
    }

}

我想如果所选的OU中有足够的计算机,这可能会对性能产生很大的影响。我应该限制在多少同时后台工作人员中?您将使用静态数字还是基于CPU核心数?

而且,您将如何为此实现队列?我考虑过要做这样的事情:

private int bwCount = 0;
private int bwLimit = 5; // 10, 20, 200??

private void QueryComputers()
{
    int stopAt = lastIndex + (bwLimit - bwCount);
    if (stopAt > Computers.Count - 1) stopAt = Computers.Count - 1;
    if (stopAt > lastIndex)
    {
        for (int i = lastIndex; i <= lastIndex + (bwLimit - bwCount); i++) {
            BackgroundWorker bw = new BackgroundWorker();
            bw.WorkerReportsProgress = true;
            bw.DoWork += BackgroundWorker_DoWork;
            bw.ProgressChanged += BackgroundWorker_ProgressChanged;
            bw.RunWorkerCompleted += BackgroundWorker_RunWorkerCompleted;
            bw.RunWorkerAsync(Computers[i].DNSHostName);

            lastIndex = i;
            bwCount++;
        }
    }
}

private void BackgroundWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
    // Handle Result etc...

    bwCount--;
    QueryComputers();
}

编辑:

尝试使用任务并行库

我从我的应用程序中采用了一种方法,该方法从远程计算机上检索登录的用户,并尝试使用TPL代替后台工作者。问题是它没有异步运行,并且UI在运行时挂起。

private void GetLoggedOnUsersTPL()
{
    Parallel.ForEach(Computers, (computer) =>
    {
        using (PowerShell ps = PowerShell.Create())
        {

            computer.Status = RemoteComputer.ComputerStatus.UpdatingStatus;

            // Ping the remote computer to check if it's available to connect to
            ps.AddScript($"Test-Connection -ComputerName {computer.DNSHostName} -Count 1 -Quiet");
            Collection<PSObject> psOutput = ps.Invoke();
            if ((bool)psOutput[0].BaseObject) // If the computer responded to the Ping
            {
                ps.Commands.Clear(); // Remove the Test-Connection (Ping) command

                // Use a WMI query to find out who is logged on to the remote computer
                ps.AddScript($"Get-CimInstance -ComputerName {computer.DNSHostName} -Class Win32_ComputerSystem -Property UserName");
                psOutput = ps.Invoke();

                if (psOutput.Count < 1) // If there are no results we will try using a DCOM connection instead of WSMAN
                {
                    ps.Commands.Clear();
                    ps.AddScript("$opt = New-CimSessionOption -Protocol DCOM");
                    ps.AddScript($"$cims = New-CimSession -ComputerName {computer.DNSHostName} -SessionOption $opt");
                    ps.AddScript($"Get-CimInstance -Class Win32_ComputerSystem -Property UserName -CimSession $cims");
                    psOutput = ps.Invoke();
                }

                if (psOutput.Count > 0) // Check if we had any results
                {
                    string userName = psOutput[0].Members["UserName"].Value.ToString();
                    if (userName == null || userName == "")
                    {
                        computer.LoggedOnUser = "Nobody is logged on...";
                        computer.Status = RemoteComputer.ComputerStatus.Online;
                    }
                    else
                    {
                        computer.LoggedOnUser = userName;
                        computer.Status = RemoteComputer.ComputerStatus.Online;

                    }
                }
                else
                {
                    computer.Status = RemoteComputer.ComputerStatus.Blocked;
                }

            }
            else
            { 
                computer.Status = RemoteComputer.ComputerStatus.Offline;
            }
        }
    });
}

我尝试制作方法async,即private async void GetLoggedOnUsersTPL(),但是这告诉我我需要使用await,在此示例中我不确定在哪里使用它。

c# multithreading backgroundworker
1个回答
0
投票

我有一个应用程序,可以将财务记录从一个总帐数据库移动到WPF应用程序中的另一个数据库。每个操作都是独立的,并且在后台线程上运行,并且有时会恢复原状或处于休眠状态,并报告给wpf应用程序,后者会尽职地记录其实时状态。

在测试期间,我想最终限制所有操作,以确保操作顺畅。

该限制实施了[[从未,我将应用发布到了生产环境,在那里有不同的人针对他们的特定模式运行该应用”。

所以我的建议是做类似的事情,您可以运行200个以上的线程来进行内部异步操作而不会费劲...因此,这取决于操作的负载以及它们在做什么,这更多地取决于这些负载。决定比一个特定的数字。
© www.soinside.com 2019 - 2024. All rights reserved.