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

时间:2012-05-12 16:31:02

标签: c# multithreading

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

  

跨线程操作无效:从a访问控制'lblp4'   除了创建它的线程以外的线程。

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

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 个答案:

答案 0 :(得分:4)

正如异常所示,您正在从创建控件的线程以外的线程访问控件(具体来说,您的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 :(得分:1)

就我而言,目的是更改组件“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);
      }
   }
}

调用者来自 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;
}

答案 2 :(得分:0)

我能看到的主要问题是从线程

访问用户界面