О многопоточности в .NET - PullRequest
0 голосов
/ 14 июля 2011

Ниже приведен пример кода, представляющий дизайн формы Windows, которую я использую в приложении Windows CE.

У нас возникли нерешенные проблемы в нашем приложении, и я подозреваю, что проблема связана с используемым потокомздесь как фоновый рабочий (поскольку класс BackgroundWorker недоступен в Windows CE).

Вы можете видеть, что объект блокировки используется для предотвращения нескольких экземпляров MyWorker.

Isэто правильный способ предотвратить множественные случаи такого "работника"?Будет ли это работать, как ожидалось?Будет ли рабочий-одиночка лучше?

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 голосов
/ 14 июля 2011

Вам вообще не нужно использовать блокировку, поскольку метод btn_Click будет вызываться только потоком пользовательского интерфейса (если вы явно не вызовете его из другого потока, что было бы плохо).

Таким образом, код нуждается только в проверке 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();   
        }
    }
}

РЕДАКТИРОВАТЬ: В ответ на комментарий от ОП.

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();
    }
}
0 голосов
/ 14 июля 2011

Я бы не использовал 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 () всякий раз, когда ваша нить делает свое дело.

...