2011-07-14 7 views
0

以下は、Windows CEアプリケーションで使用するWindowsフォームのデザインを示すコードサンプルです。.NETのスレッディングについて

私たちのアプリケーションにはいくつかの未解決の問題があります。この問題はバックグラウンドワーカーとしてここで使用されているスレッドから発生していると思われます(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 CEデバイスにはボタンが1つのみのWindowsフォームがあります。ユーザーがボタンをクリックすると、WCFサービスを要求するワーカースレッドを開始する必要があります。スレッドの実行中、小さな砂時計は、プログラムがビジーであることをユーザーに示します。作業者が完了したとき(つまり、「良い」回答を受け取ったとき)は、現在のウィンドウの上にある別のWindowsフォームを停止して開く必要があります(そのために、メソッドInvokeを使用して、形)。

唯一の要件は、は、ボタンを2回クリックするなどして2人の作業者を実行できないようにすることです。実際には、2人のワーカーが同じデバイスからWCFサービスへのリクエストを行っていることが時々分かります。

+0

あなたが持っているものを問題としていません。また、C#プログラミング言語にはスレッド固有の構成要素がありません。あなたは実際にC#のスレッドではなく.NETのスレッドについて質問しています。 –

+0

アプリケーション全体が私たちの問題の原因となる可能性のあるWCFを使用しているため、問題はコードサンプルでは説明が少し難しいです。この投稿の目的は、コードのこの部分が「安全」であることを確認することです。そしてタイトルを変更しました。 – Goulutor

+0

私はあなたが解決しようとしている問題を教えてくれていないということです。 –

答えて

0

この場合、lock()は使用しません。バックグラウンドワーカーがすでに実行されている場合、メインのGUIスレッドは、他のスレッドが終了するまでそのロックの上に座ります。

代わりにMutexを使用することをお勧めします。このようにして、セクションが既にロックされている場合は、セクションを自動的にスキップするように設定できます。あなたのスレッドは、そのようなものをやって行われている時はいつでもあなたはmtx.Release()を行う必要があります

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? 
    } 
} 

だからあなたのような何かを行う必要があります。

+0

ロックが正しく使用された場合、これは、isWorkerStartedフラグの設定で競合状態を防止するためにのみ必要であるためです。本当の問題は、スレッドがロックを取得して保持することです。実際にロックを取得して保持する必要がない場合です。 –

+0

さて、これを振り返ってみると、それは本当です。彼がちょうどそれを移動した場合、ロックの外にあるウィンドウと対話するためにいくつかの代理人を呼び出します。私は何らかの理由でisWorkerStartedブールを完全に見逃していました。そして、彼は複数のインスタンスを実行するためにロックを使用していると思っていました。ああ、私もうまくいくよ! :) – DanTheMan

+0

既存のSyncBlock(lock文の実装メカニズム)を使用して、このパターンを実装することもできます。これは、Monitor.TryEnterメソッドがあるためです。トリッキーなことは、あなたがそれを取得することができる場合は常にロックを解除することです。私はこれがMutexよりも軽いと信じていますが、Mutexもうまく動作するはずです。 –

0

btn_ClickメソッドはUIスレッドによってのみ呼び出されるため(ここでは、明示的に別のスレッドから呼び出さない限り)、ロックを使用する必要はありません。

したがって、コードにはnullのチェックが必要なので、1つのワーカースレッドしか作成されていないことを確認してください。

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

EDIT: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(); 
    } 
} 
+0

あなたのソリューションでは、後で別のワーカーを作成できるようにしたいので、ワーカーがその仕事を終了したことをどのように確認できますか? – Goulutor

+0

@ Goulutor - あなたの元のコードは、スレッドが終了したときにチェックする方法がありませんでした。あなたの質問では、ワーカーの複数のインスタンスが必要ではないと言いました。あなたの質問が「コード完成」ではないと感じました。私に目をつけて、私のソリューションを編集します。 – Enigmativity

+0

Compact Framework(Windows CE上)を使用しているため、クラスThreadのIsAliveプロパティは存在しません。 – Goulutor