我正在处理的问题涉及使用功能性锁或监视器结构,以便在单独的线程上仅提供对一个成员的独占访问。下面是我对监视器的类定义(注意它与c#库给出的实际监视器类不同)。我想要做的是让我的表格上出现或消失图片框。
我试图添加一个表单实例,以便我可以访问各个图片框,但是,我的程序似乎冻结了。
namespace SmokersProblem
{
class monitor
{
Form1 form = new Form1();
Random num = new Random();
object obj = new object();
public monitor()
{
}
public void agent(){
form.pictureBox4.Visible = false;
int r = num.Next(1, 4);
if (r == 1)
{
// put lighter and paper on table
smoker1();
}
else if (r == 2)
{
// put lighter and tobacco on table
smoker2();
}
else
{
// put paper and tobacco on table
smoker3();
}
}
public void smoker1()
{
//lock (obj)
//{
form.pictureBox9.Visible = true;
form.pictureBox1.Visible = false;
System.Threading.Thread.Sleep(5000);
//agent();
// }
}
public void smoker2()
{
//lock (obj)
//{
form.pictureBox10.Visible = true;
form.pictureBox3.Visible = false;
System.Threading.Thread.Sleep(5000);
//agent();
//}
}
public void smoker3()
{
//lock (obj)
//{
form.pictureBox11.Visible = true;
form.pictureBox2.Visible = false;
System.Threading.Thread.Sleep(5000);
//agent();
// }
}
}
}
下面是我的表单代码,正如你在这里看到的,我尝试创建三个单独的线程,每个吸烟者一个。
namespace SmokersProblem
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
Random rnd = new Random();
int num = rnd.Next(1, 4);
Object obj = new Object();
}
private void Form1_Load(object sender, EventArgs e)
{
}
private void button1_Click(object sender, EventArgs e)
{
pictureBox1.Visible = true;
pictureBox2.Visible = true;
pictureBox3.Visible = true;
pictureBox8.Visible = false;
pictureBox7.Visible = false;
pictureBox6.Visible = false;
monitor one = new monitor();
one.agent();
Thread vone = new Thread(one.smoker1);
Thread two = new Thread(one.smoker2);
Thread three = new Thread(one.smoker3);
vone.Start();
two.Start();
three.Start();
}
}
}
答案 0 :(得分:0)
我的程序似乎冻结了
我的one.agent()是允许其中一个吸烟者的代码的一部分 被召唤,所以他们可以吸烟。为什么我不想在主要的 码?
因为您不应该从主UI线程使用Sleep(),这是从Button Click事件调用one.agent()时发生的情况。当Sleep(5000)被击中时,你告诉表单主UI线程在5秒内没有做任何事情,因此你看到了冻结。
要解决这个问题,你需要agent()在一个单独的线程中执行smoker1(),smoker2()或smoker3(),就像你在下面做的那样。
代码还有其他一些问题,但是,在您“修复”代码之前,还必须解决这些问题......
下一个问题在于在monitor()类中创建Form1()的新实例。 Form1()的此实例与屏幕上显示的实例不同。根据它来修改一个从未显示过的隐形表格。要对屏幕上实际可见的表单进行操作,您需要(a)在创建它时将引用传递给monitor()类,或者(b)让monitor()类引发自定义事件Form1()在创建monitor()时再次订阅。
最后一个问题在于您尝试从主UI线程以外的线程中更改UI控件。这将导致跨线程异常(除非你关闭它,你不应该这样做)。有多种方法可以解决这个问题,其中最基本的方法是使用委托和您尝试更新的Form / Control的Invoke()方法。
答案 1 :(得分:0)
执行此操作后,我去寻找OP正在尝试实施的Smoker Thread Problem。此代码应该很容易适应该问题。
你的用户界面冻结的原因是你在调用one.agent()
而不将其放入新线程中。 one.agent()
会休眠,这会阻止您的UI处理事件。
好的,我已经实现了一些代码来处理带标签的吸烟者问题。显然它可以改进,例如通过不将表单耦合到线程。
我放了两个不同的锁定机制,并留下一个注释掉。
基本上,有三个标签可以是“吸烟”或“不吸烟”。主UI线程创建三个线程:
每个线程不断尝试在while循环中获取锁定。当他们拿锁时,他们将标签设置为“吸烟”,等待几秒钟,然后将其标签设置为“不吸烟”。这使用来自this answer的线程安全代码。
public partial class Form1 : Form
{
private bool running = false;
public Label OneLabel { get; set; }
public Label TwoLabel { get; set; }
public Label ThreeLabel { get; set; }
private MyMonitor one;
private Thread vone;
private Thread two;
private Thread three;
public Form1()
{
InitializeComponent();
OneLabel = new Label();
OneLabel.Text = "Not Smoking";
OneLabel.Location = new Point(10, 50);
OneLabel.AutoSize = true;
this.Controls.Add(OneLabel);
TwoLabel = new Label();
TwoLabel.Text = "Not Smoking";
TwoLabel.Location = new Point(150, 50);
this.Controls.Add(TwoLabel);
ThreeLabel = new Label();
ThreeLabel.Text = "Not Smoking";
ThreeLabel.Location = new Point(300, 50);
this.Controls.Add(ThreeLabel);
}
private void MainButton_Click(object sender, EventArgs e)
{
if (!running)
{
vone.Start();
two.Start();
three.Start();
MainButton.Text = "Stop";
running = true;
}
else
{
one.RequestStop();
MainButton.Text = "Run";
running = false;
}
}
private void Form1_Load(object sender, EventArgs e)
{
one = new MyMonitor(this);
vone = new Thread(one.Smoker1);
two = new Thread(one.Smoker2);
three = new Thread(one.Smoker3);
}
private void Form1_FormClosing(object sender, FormClosingEventArgs e)
{
if (running)
{
one.RequestStop();
running = false;
}
}
}
class MyMonitor
{
private int x = 1;
private Object obj = new Object();
private Form1 _form;
bool _finished = false;
public MyMonitor(Form1 form)
{
_form = form;
}
public void Smoker1()
{
while (!_finished)
{
//lock (obj)
//{
// _form.OneLabel.SetPropertyThreadSafe(() => _form.OneLabel.Text, "Smoking");
// System.Threading.Thread.Sleep(2000);
// _form.OneLabel.SetPropertyThreadSafe(() => _form.OneLabel.Text, "Not Smoking");
//}
try
{
Monitor.Enter(obj);
try
{
_form.OneLabel.SetPropertyThreadSafe(() => _form.OneLabel.Text, "Smoking");
System.Threading.Thread.Sleep(2000);
_form.OneLabel.SetPropertyThreadSafe(() => _form.OneLabel.Text, "Not Smoking");
}
finally
{
Monitor.Exit(obj);
}
}
catch (SynchronizationLockException SyncEx)
{
Console.WriteLine(SyncEx.Message);
}
}
}
public void Smoker2()
{
while (!_finished)
{
//lock (obj)
//{
// _form.TwoLabel.SetPropertyThreadSafe(() => _form.TwoLabel.Text, "Smoking");
// System.Threading.Thread.Sleep(2000);
// _form.TwoLabel.SetPropertyThreadSafe(() => _form.TwoLabel.Text, "Not Smoking");
//}
try
{
Monitor.Enter(obj);
try
{
_form.TwoLabel.SetPropertyThreadSafe(() => _form.TwoLabel.Text, "Smoking");
System.Threading.Thread.Sleep(2000);
_form.TwoLabel.SetPropertyThreadSafe(() => _form.TwoLabel.Text, "Not Smoking");
}
finally
{
Monitor.Exit(obj);
}
}
catch (SynchronizationLockException SyncEx)
{
Console.WriteLine(SyncEx.Message);
}
}
}
public void Smoker3()
{
while (!_finished)
{
//lock (obj)
//{
// _form.ThreeLabel.SetPropertyThreadSafe(() => _form.ThreeLabel.Text, "Smoking");
// System.Threading.Thread.Sleep(2000);
// _form.ThreeLabel.SetPropertyThreadSafe(() => _form.ThreeLabel.Text, "Not Smoking");
//}
try
{
Monitor.Enter(obj);
try
{
_form.ThreeLabel.SetPropertyThreadSafe(() => _form.ThreeLabel.Text, "Smoking");
System.Threading.Thread.Sleep(2000);
_form.ThreeLabel.SetPropertyThreadSafe(() => _form.ThreeLabel.Text, "Not Smoking");
}
finally
{
Monitor.Exit(obj);
}
}
catch (SynchronizationLockException SyncEx)
{
Console.WriteLine(SyncEx.Message);
}
}
}
public void RequestStop()
{
_finished = true;
}
}
//Thread Safe Extension Method
public static class Extensions
{
private delegate void SetPropertyThreadSafeDelegate<TResult>(Control @this, Expression<Func<TResult>> property, TResult value);
public static void SetPropertyThreadSafe<TResult>(this Control @this, Expression<Func<TResult>> property, TResult value)
{
var propertyInfo = (property.Body as MemberExpression).Member as PropertyInfo;
if (propertyInfo == null ||
!@this.GetType().IsSubclassOf(propertyInfo.ReflectedType) ||
@this.GetType().GetProperty(propertyInfo.Name, propertyInfo.PropertyType) == null)
{
throw new ArgumentException("The lambda expression 'property' must reference a valid property on this Control.");
}
if (@this.InvokeRequired)
{
@this.Invoke(new SetPropertyThreadSafeDelegate<TResult>(SetPropertyThreadSafe), new object[] { @this, property, value });
}
else
{
@this.GetType().InvokeMember(propertyInfo.Name, BindingFlags.SetProperty, null, @this, new object[] { value });
}
}
}