2

首先,对不起我的英语语言不好,我是初学者程序员。在我的项目中,我使用线程类,但是完成后我看到了这个异常:

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

在我的项目中,我SynchronizePhilosopher类中调用 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);
    }
}

}

4

3 回答 3

6

正如异常所暗示的,您正在从创建控件的线程以外的线程访问控件(具体来说,您的 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...");
于 2012-05-12T16:42:08.153 回答
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);
      }
   }
}

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;
}
于 2021-06-03T23:48:57.217 回答
0

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

于 2012-05-12T16:43:35.000 回答