下面是一个代码示例,展示了我在Windows CE应用程序中使用的Windows窗体的设计。
我们的应用程序中存在一些未解决的问题,我怀疑问题来自此处用作后台工作程序的线程(因为在Windows CE中没有类BackgroundWorker
)。
您可以看到锁定对象用于阻止MyWorker
的多个实例。
这是防止这种“工人”的多个实例的正确方法吗?它会按预期工作吗?单身工人会更好吗?
public class MainForm : Form {
private object myLock = new Object();
private bool isWorkerStarted = false;
private Thread worker;
public MainForm() {
}
public void btn_Click() {
lock(myLock) {
if(!isWorkerStarted) {
MyWorker worker = new MyWorker();
worker.StartEvent = new EventHandler(ThreadStart);
worker.EndEvent = new EventHandler(ThreadStop);
workerThread = new Thread(worker.DoWork);
workerThread.Start();
isWorkerStarted = true;
}
}
}
public void ThreadStart(object sender, EventArgs args) {
lock(myLock) {
isWorkerStarted = true;
// Invoke some delegate to interact with the window
}
}
public void ThreadStop(object sender, EventArgs args) {
lock(mylock) {
isWorkerThread = false;
}
this.Invoke(new NewFormDelegate(OpenForm));
}
private void OpenForm() {
AnotherWindowForm awf = new AnotherWindowForm();
awf.Show();
this.Close();
}
//******************
// Worker class
//******************
public class MyWorker() {
public event EventHandler StartEvent;
public void OnStart() {
if(StartEvent != null) {
StartEvent(this, new EventArgs());
}
}
// Edit 2011-07-19
public void OnEnd() {
if(EndEvent != null) {
EndEvent(this, new EventArgs());
}
}
public void DoWork() {
OnStart();
// Do some work.
OnEnd();
}
}
}
编辑2011-07-19 以下是有关该项目目标的更多详细信息。
我们有一个Windows窗体,在Windows CE设备上只有一个按钮。当用户单击该按钮时,必须启动工作线程以请求WCF服务。在执行线程期间,小沙漏向用户指示程序正忙。当工作人员完成时(即当他收到“好”答案)时,它必须停止并在当前工作的顶部打开另一个Windows表单(为此我正在使用方法Invoke
委派创建新表格。)
唯一的要求是用户无法运行两个工作人员(例如,通过在按钮上单击两次)。事实是,有时,我们可以看到两个工作人员正在从同一设备向WCF服务发出请求。
答案 0 :(得分:0)
在这种情况下我不会使用lock()。如果后台工作程序已在运行,则主GUI线程将位于该锁上,直到另一个线程完成。
我建议改用Mutex。通过这种方式,您可以将其设置为在已经锁定的情况下自动跳过该部分。
所以你应该这样做:
Mutex mtx = new Mutex();
public void btn_Click() {
if ( mtx.WaitOne(0) == true )
{
//Do your stuff
}
else
{
//Let user know you can't do this yet? Or, queue it up? Or, why
// can the user click this button if they can't do anything anyway?
}
}
每当你的线程完成它的工作时,你需要做一个mtx.Release()。
答案 1 :(得分:0)
你根本不需要在这里使用锁,因为btn_Click
方法只会被UI线程调用(除非你从另一个可能不好的线程中明确地调用它)。
因此代码只需要进行null
检查,以确保只创建一个工作线程。
public class MainForm : Form
{
private Thread workerThread = null;
public MainForm()
{ }
public void btn_Click()
{
if (workerThread == null)
{
var worker = new MyWorker();
worker.StartEvent += (s, e) =>
{
//Invoke some delegate to interact with the window
};
workerThread = new Thread(worker.DoWork);
workerThread.Start();
}
}
}
编辑:回应OP的评论。
public class MainForm : Form
{
private Thread workerThread = null;
public MainForm()
{ }
public void btn_Click()
{
if (workerThread == null || workerThread.IsAlive == false)
{
var worker = new MyWorker();
worker.StartEvent += (s, e) =>
{
//Invoke some delegate to interact with the window
};
worker.EndEvent += (s, e) =>
{
//Clean up
};
workerThread = new Thread(worker.DoWork);
workerThread.Start();
}
}
}
MyWorker
现在看起来像这样:
public class MyWorker
{
public event EventHandler StartEvent;
public event EventHandler EndEvent;
public void OnStart()
{
var se = this.StartEvent;
if (se != null)
{
se(this, new EventArgs());
}
}
public void OnEnd()
{
var ee = this.EndEvent;
if (ee != null)
{
ee(this, new EventArgs());
}
}
public void DoWork()
{
this.OnStart();
// do some work
this.OnEnd();
}
}