首先,抱歉我的英语很弱,我是初学程序员。在我的项目中,我使用线程类,但完成后我看到了这个异常:
跨线程操作无效:从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);
}
}
}
答案 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)
我能看到的主要问题是从线程
访问用户界面