当创建一个新线程时,不会对GUI进行更改(c#)

本文关键字:GUI 创建 一个 新线程 线程 | 更新日期: 2023-09-27 18:18:21

在一些帮助下,我设法创建了一个新线程,虽然方法似乎要执行,方法的条件要么使绿色或红灯出现,虽然在没有新线程的情况下运行方法(Check1..等),这些变化反映在GUI上(例如红/绿灯出现),但是当创建一个新线程并运行方法时,这些变化不反映在Form/GUI上。

// Method / Action to start the checks         private void StartChecks_Click(object sender, EventArgs e)
        {
            Thread t = new Thread(
               o =>
               {
                   InitChecks();
               });
            t.Start();
        }
// Check1 public void Check1()
        {
            // lets grabs the info from the config!
            var lines = File.ReadAllLines("probe_settings.ini");
            var dictionary = lines.Zip(lines.Skip(1), (a, b) => new { Key = a, Value = b })
                                  .Where(l => l.Key.StartsWith("#"))
                                  .ToDictionary(l => l.Key, l => l.Value);
            // lets set the appropriate value for this check field
            label1.Text = dictionary["#CheckName1"];
            // lets define variables and convert the string in the dictionary to int for the sock.connection method!
            int portno1;
            int.TryParse(dictionary["#PortNo1"], out portno1);
            // Convert hostname to IP, performance issue when using an invalid port on a hostname using the TcpClient class! 
            IPAddress[] addresslist = Dns.GetHostAddresses(hostname2);
            foreach (IPAddress theaddress in addresslist)
            {
                // Attempt to create socket and connect to specified port on host
                TcpClient tcP = new System.Net.Sockets.TcpClient();
                try
                {
                    tcP.ReceiveTimeout = 1;
                    tcP.SendTimeout = 1;
                    tcP.Connect(theaddress, portno1);
                    displayGreen1();
                    tcP.Close();
                }
                catch
                {
                    displayRed1();
                }
            }
        }
// Change the lights when the condition is met
        public void displayGreen1()
        {
            pictureBox2.Visible = false;
            pictureBox1.Visible = true;
        }
        private void displayRed1()
        {
            pictureBox2.Visible = true;
            pictureBox1.Visible = false;
        }

当创建一个新线程时,不会对GUI进行更改(c#)

这就是WinForms的设计方式。您不能从另一个线程进行更改。解决方案通常是使用异步委托。

首先添加这个声明

 public delegate void MyDelegate1 ();
 public delegate void MyDelegate2 (); 

然后当你在另一个线程中,你应该做:

MyDelegate1 d1= new MyDelegate1 (displayGreen1);
 this.BeginInvoke(d1);
 MyDelegate2 d2= new MyDelegate2 (displayRed1);
 this.BeginInvoke(d2);

这是因为UI控件只能从UI线程更新。当你没有创建一个新线程时,更新控件的代码在UI线程上运行,所以它像你期望的那样工作。当你创建一个新线程时,因为这个线程不是 UI线程,应该更新控件的代码失败了。

您可以通过更改方法调用来确保更新控件的代码在UI线程上运行:-

this.BeginInvoke(new Action(() => displayRed1()));

this.BeginInvoke(new Action(() => displayGreen1()));

顺便说一句(与你当前的问题无关):-

尽量避免创建显式线程。相反,使用线程池为您管理线程,例如ThreadPool.QueueUserWorkItem(x => InitChecks())。(请注意,这仍然将在非ui线程上运行,因此您仍然需要使用上面详细介绍的BeginInvoke()。)线程池最清楚何时创建和执行线程,使用它最终会使你的代码更高效。

尽量避免捕获所有异常类型在您的try{...}catch{...}。这表明当抛出任何类型的异常时,您的代码知道该做什么。相反,只捕获您确切知道如何处理的异常,

try
{
    ...
}
catch(TcpException)
{
    ...
}
catch(AnotherKnownException)
{
    ...
}
...

注意,对于所有异常类型也可以使用catch块,只要在退出块时重新抛出异常,

try
{
    ...
}
catch(KnownException)
{
    ...
}
catch(Exception)
{
    // perform some logging, rollback, etc.
    throw;
}

以你的技能水平,最好是:

  • 在表单中使用计时器来检查一些状态变量(例如bool _pb1Visiblebool _pb2Visible)
  • 在timer事件中更新picturebox可见性
  • 在线程中,只更新上述bool成员变量。

它会像一个魅力!

简单的例子:

Check1()方法中,而不是:

displayGreen1();

_pb1Visible=true;
_pb1Visible=false;

而不是

displayRed1();

_pb1Visible=false;
_pb1Visible=true;

将计时器放在表单上。在计时器事件中,执行:

pictureBox2.Visible = _pb2Visible;
pictureBox1.Visible = _pb1Visible;