如何使用一个线程来完成,然后重用线程

本文关键字:线程 然后 一个 何使用 | 更新日期: 2023-09-27 18:14:46

我正在构建一个表单,我试图使用线程,以便从WMI查询中获得一些结果,以显示在文本框中,而无需表单冻结在用户上。然而,当我使用下面的代码并在调试时使用Break-All时,代码只是位于getPrinterThread.Join()上。我知道我一定错过了什么。

我的目标是获得一个线程来运行ObtainPrinterPort方法完成,然后获得一个线程来运行InstallPrinterPort方法完成。我有下面的代码作为内联代码在另一个方法。代码不是在一个单独的类或任何东西中,我没有后台工作器,因为到目前为止,我看到的所有示例都只会让我感到困惑。

这是我公认的糟糕的线程尝试:

Thread printThread = new Thread(ObtainPrinterPort);
printThread.Start();
while (!printThread.IsAlive) ;
Thread.Sleep(1);
printThread.Join();         // Form sits and does nothing; Break-all reveals this line as statement being executed.
Thread installThread = new Thread(InstallPrinterPort);
installThread.Start();
while (!installThread.IsAlive);
Thread.Sleep(1);
installThread.Join();

是否有一个简单的方法,我可以得到一些工作,是安全的,并允许我显示的结果发生在方法,因为他们发生在文本框中的用户?希望有一种方法来做到这一点,将允许我继续使用实例变量/方法/代码我已经写在表单类…否则,如果我要实现一个"DoWork"类型的例子(我的方法是从DoWork方法/构造函数或Worker类调用的),我将不得不重写很多代码。

请记住,我的方法需要从线程返回文本到文本框,以显示结果给用户。我有代码,我假设将允许我从线程返回文本,如果它的工作,但我只是想确保任何建议/帮助记住这一点。我使用的代码如下:

public void AppendTextBox(string value)
{
    if (InvokeRequired)
    {
        this.Invoke(new Action<string>(AppendTextBox), new object[] { value });
        return;
    }
    txtResults.Text += value;
}

为了它的价值,这里是我的ObtainPrinterPort方法和伴随它的CreateNewConnection方法…InstallPrinterPort方法非常相似,所以发布它并不能真正揭示太多:

private ManagementScope CreateNewConnection(string server, string userID, string password)
{
    string serverString = @"''" + server + @"'root'cimv2";
    ManagementScope scope = new ManagementScope(serverString);
    try
    {
        ConnectionOptions options = new ConnectionOptions
        {
            Username = userID,
            Password = password,
            Impersonation = ImpersonationLevel.Impersonate,
            EnablePrivileges = true
        };
        scope.Options = options;
        scope.Connect();
    }
    catch (ManagementException err)
    {
        MessageBox.Show("An error occurred while querying for WMI data: " +
                        err.Message);
    }
    catch (System.UnauthorizedAccessException unauthorizedErr)
    {
        MessageBox.Show("Connection error (user name or password might be incorrect): " +                              unauthorizedErr.Message);
    }
    return scope;
}
private void ObtainPrinterPort()
{
    string computerName = "";
    string userID = "";
    string password = "";
    string printerQuery = "SELECT * FROM Win32_Printer WHERE Name = ";
    string portQuery = "SELECT * FROM Win32_TCPIPPrinterPort WHERE Name = ";
    string search = "";
    SelectQuery query;
    foreach (var s in lstServer)
    {
        computerName = s.ServerName;
        userID = s.UserID;
        password = s.Password;
    }
    ManagementScope scope = CreateNewConnection(computerName, userID, password);
    foreach (Printers p in lstPrinters)
    {
        AppendTextBox("Obtaining printer/port info for " + p.PrinterName + "'r'n");
        search = printerQuery + "'" + p.PrinterName + "'";
        query = new SelectQuery(search);
        try
        {
            using (var searcher = new ManagementObjectSearcher(scope, query))
            {
                ManagementObjectCollection printers = searcher.Get();
                if (printers.Count > 0)
                {
                    AppendTextBox("'tStoring printer properties for " + p.PrinterName + "'r'n");
                    foreach (ManagementObject mo in printers)
                    {
                        StorePrinterProperties(p, mo);
                    }
                }
                else
                {
                    lstPrinterExceptions.Add("Printer: " + p.PrinterName);
                    AppendTextBox("'t**Printer " + p.PrinterName + " not found**'r'n");
                }
            }
        }
        catch (Exception exception)
        {
            MessageBox.Show("Error: " + exception.Message, "Error",
                            MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
        }
        if (!lstPrinterExceptions.Contains("Printer: " + p.PrinterName)
           && !lstPrinterExceptions.Contains("Port: " + p.PortName))
        {
            search = portQuery + "'" + p.PortName + "'";
            query = new SelectQuery(search);
            try
            {
                using (var searcher = new ManagementObjectSearcher(scope, query))
                {
                    ManagementObjectCollection ports = searcher.Get();
                    if (ports.Count > 0)
                    {
                        AppendTextBox("'tStoring port properties for " + p.PortName + " (" + p.PrinterName + ")'r'n");
                        foreach (ManagementObject mo in ports)
                        {
                            StorePortProperties(p, mo);
                        }
                    }
                    else
                    {
                        lstPrinterExceptions.Add("Port: " + p.PortName);
                        AppendTextBox("'t**Port " + p.PortName + " for " + p.PrinterName + " not found**'r'n");
                    }
                }
            }
            catch (Exception exception)
            {
                MessageBox.Show("Error: " + exception.Message, "Error",
                                MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
            }
            AppendTextBox("'tSuccessfully obtained printer/port info for " + p.PrinterName + "'r'n");
        }
    }
}

谢谢。

如何使用一个线程来完成,然后重用线程

你的问题是Invoke调用阻塞等待主线程到DoEvents,所以它们可以被处理,但主线程阻塞在Thread.Join。出现死锁。

这是你如何运行一个线程而不阻塞UI线程。Thread.Join阻塞直到其他线程完成,所以在这里我只阻塞最大100ms,然后调用DoEvents,这样表单就可以响应消息(包括处理来自其他线程的Invoke调用),然后循环直到后台线程完成。

Thread printThread = new Thread(ObtainPrinterPort);
printThread.Start();
while (printThread.IsAlive) {
    Application.DoEvents();        
    printThread.Join(100);        
}

在这样的循环中调用DoEvents有点粗糙,但它会工作。

您还可以查看BackgroundWorker,它使整个事情更安全,更容易。

一个简单的例子:

var bw = new BackgroundWorker();            
bw.DoWork += (worker, args) => {
    ObtainPrinterPort();
};
bw.RunWorkerAsync();