C#中的线程异常错误:跨线程操作无效:从不正确的线程访问控件'lblp4'。

3

首先,对于我的英语水平我很抱歉,我是一名初级程序员。在我的项目中,我使用了线程类,但当我完成后,我看到了这个异常:

跨线程操作无效: 从一个不是创建控件"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);
    }
}

}

3个回答

7

正如异常所示,你正在从一个不同于创建控件的线程访问控件(具体来说,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...");

1
在我的情况下,意图是改变一个组件“Janus.Windows.Ribbon.CommandBase”的行为(适用于任何其他类型),以便从另一个表单启用或禁用。而行为的更改是通过监听组件更改的onchange方法完成的。
根据微软文档(https://learn.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;
}

0

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


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接