繁体   English   中英

C# 中的线程异常错误:跨线程操作无效:从跨线程操作访问的控制“lblp4”无效

[英]Thread exception error in C# :Cross-thread operation not valid: Control 'lblp4' accessed from a Cross-thread operation not valid

首先,抱歉我的英语能力很差,我是初学者程序员。 在我的项目中,我使用线程类,但完成后我看到这个异常:

跨线程操作无效:控制“lblp4”从创建它的线程以外的线程访问。

在我的项目中,我从Form1类中的Philosopher类调用 5 个函数Synchronize

Philosopher课:

namespace AZsys 
{
class Philosopher 
{
    public Int32 i;

    public bool e, th;
    public Philosopher()
    {

    }
    public void main()
    {
        lock (AZsys.Program.frm.locker)
        {
            while (true)
            {
                if (i < 0 || i > 4)
                {
                    System.Windows.Forms.MessageBox.Show("error");
                    break;
                }
                else
                {
                    think();
                    AZsys.Program.frm.chopstick[i].WaitOne();
                    AZsys.Program.frm.chopstick[(i + 1) % 5].WaitOne();
                    eat();
                    AZsys.Program.frm.chopstick[(i + 1) % 5].Release();
                    AZsys.Program.frm.chopstick[i].Release();
                }
            }
        }
        Thread.Sleep(100);
    }
    private void eat()
    {
            switch (i)
            {
                case 1:
                    AZsys.Program.frm.lblp1.Text = "Eating...";
                    break;
                case 2:
                    AZsys.Program.frm.lblp2.Text = "Eating...";
                    break;
                case 3:
                    AZsys.Program.frm.lblp3.Text = "Eating...";
                    break;
                case 4:
                    AZsys.Program.frm.lblp4.Text = "Eating...";
                    break;
                case 5:
                    AZsys.Program.frm.lblp5.Text = "Eating...";
                    break;
            }
        e = true;

        for (int j = 0; j < 992; j++)
        {
            if (j % 8 == 0)
                e = true;
        }
        e = false;
    }

    private void think()
    {
            switch (i)
            {
                case 1:
                    AZsys.Program.frm.lblp1.Text = "Thinking..."+Thread.CurrentThread.Name.ToString();
                    break;
                case 2:
                    AZsys.Program.frm.lblp2.Text = "Thinking..."+Thread.CurrentThread.Name.ToString();
                    break;
                case 3:
                    AZsys.Program.frm.lblp3.Text = "Thinking..."+Thread.CurrentThread.Name.ToString();
                    break;
                case 4:
                    AZsys.Program.frm.lblp4.Text = "Thinking..."+Thread.CurrentThread.Name.ToString();
                    break;
                case 5:
                    AZsys.Program.frm.lblp5.Text = "Thinking..." + Thread.CurrentThread.Name.ToString();
                    break;
            }

        th = true;

        for (int j = 0; j < 9924; j++)
        {
            if (j % 8 == 0)
                th = true;
        }
        th = false;
    }
}

即使在这段代码中,我也使用了 lock ( locker ) 但不起作用!!!

Form1类:

   public partial class Form1 : Form
{
    public  Semaphore[] chopstick;
    public  object locker;

    private Philosopher ph1;
    private Philosopher ph2;
    private Philosopher ph3;
    private Philosopher ph4;
    private Philosopher ph5;

    public Form1()
    {
        InitializeComponent();
        chopstick = new Semaphore[5];



    }

    private void Form1_Load(object sender, EventArgs e)
    {
        locker = new object();
        ph1 = new Philosopher();
        ph1.i = 1;
        ph2 = new Philosopher();
        ph2.i = 2;
        ph3 = new Philosopher();
        ph3.i = 3;
        ph4 = new Philosopher();
        ph4.i = 4;
        ph5 = new Philosopher();
        ph5.i = 5;
    }

    private void lblp2_Click(object sender, EventArgs e)
    {

    }

    private void btnstart_Click(object sender, EventArgs e)
    {
        Thread.CurrentThread.Priority = ThreadPriority.Lowest;



        Thread t1 = new  Thread(ph1.main);
        Thread t2 = new  Thread(ph2.main);
        Thread t3 = new Thread(ph3.main);
        Thread t4 = new Thread(ph4.main);
        Thread t5 = new Thread(ph5.main);


        t1.Name = "t1";

        t2.Name = "t2";

        t3.Name = "t3";

        t4.Name = "t4";

        t5.Name = "t5";


      t1.Priority = ThreadPriority.Highest;
      t2.Priority = ThreadPriority.Highest;
      t3.Priority = ThreadPriority.Highest;
      t4.Priority = ThreadPriority.Highest;
      t5.Priority = ThreadPriority.Highest;
     // Thread.Sleep(100);
        t4.Start();
        Thread.Sleep(100);
        t1.Start();
        Thread.Sleep(100);
        t2.Start();
        Thread.Sleep(100);
        t3.Start();
        Thread.Sleep(100);
        t5.Start();
        Thread.Sleep(100);
    }
}

}

正如例外情况所暗示的那样,您正在从创建控件的线程以外的线程访问控件(具体来说,您的 ph1..5 线程都尝试访问 UI)。

要纠正此问题,您需要在控件上使用Invoke()方法,以便在主 UI 线程上执行访问。

也许向Philosopher添加一个函数,如下所示:

private void UpdateText(Label label, string text)
{
    // If the current thread is not the UI thread, InvokeRequired will be true
    if (label.InvokeRequired)
    {
        // If so, call Invoke, passing it a lambda expression which calls
        // UpdateText with the same label and text, but on the UI thread instead.
        label.Invoke((Action)(() => UpdateText(label, text)));
        return;
    }
    // If we're running on the UI thread, we'll get here, and can safely update 
    // the label's text.
    label.Text = text;
}

然后,每当你有类似的东西时:

AZsys.Program.frm.lblp1.Text = "Eating...";

替换为:

UpdateText(AZsys.Program.frm.lblp1, "Eating...");

就我而言,目的是更改组件“Janus.Windows.Ribbon.CommandBase”(适用于任何其他类型)的行为,以从其他表单启用或禁用。 行为更改是通过监听组件更改的 onchange 方法完成的。

此实现是按照 Microsoft 文档 ( https://docs.microsoft.com/es-es/dotnet/desktop/winforms/controls/how-to-make-thread-safe-calls-to-)建议的步骤执行的windows-forms-controls?view = netframeworkdesktop-4.8)

首先,使用必须是要更改的属性的数据类型的参数来声明安全调用。

private delegate void SaveCallDelegate(bool enable);

用于捕获被修改组件的属性的变量

private CommandBase cm;

OnChange 事件查询是否应该安全地调用组件,如果是,它会调用“SaveCallChange”方法,并使用要更改的值。

protected override void OnChanged(Command command){
   base.OnCommandChanged(command);
   
   foreach (KeyValuePair<CommandBase, List<string>> p in Invokers)
   {
      if (p.Key.Ribbon != null && p.Key.Ribbon.InvokeRequired)
      {
         cm = p.Key;
         SaveCallChange((command.Status == CommandStatus.Enabled));
      }
      else {
         p.Key.Enabled = (command.Status == CommandStatus.Enabled);
      }
   }
}

Invokers 来自 EventCommandAdapter 类(“Microsoft.Practices.CompositeUI.Commands”),它继承了主类。

最后有两种方法可以执行安全的变更过程

private void SaveCallChange(bool enable){
    var e = new SaveCallDelegate(setEnable);
    cm.Ribbon.Invoke(e, new object[] { enable });
}

private void setEnable(bool enable) {
    this.cm.Enabled = enable;
}

我可以看到的主要问题是从线程访问用户界面

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM