关于.NET中的线程

时间:2011-07-14 15:15:19

标签: .net winforms locking thread-safety windows-ce

下面是一个代码示例,展示了我在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服务发出请求。

2 个答案:

答案 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();
    }
}