web-dev-qa-db-ja.com

スレッドが.NETで終了するのを待つ方法

C#では、メインのUIスレッドだけでなく2つのスレッドが必要になる前に、スレッド化を実際に使用したことはありません。基本的に、私は以下があります。

public void StartTheActions()
{
  //Starting thread 1....
  Thread t1 = new Thread(new ThreadStart(action1));
  t1.Start();

  // Now, I want for the main thread (which is calling `StartTheActions` method) 
  // to wait for `t1` to finish. I've created an event in `action1` for this. 
  // The I wish `t2` to start...

  Thread t2 = new Thread(new ThreadStart(action2));
  t2.Start();
}

したがって、本質的に、私の質問は、スレッドが他のスレッドの終了を待つ方法です。これを行うための最良の方法は何ですか?

158
Maxim Zaslavsky

5つの選択肢があります。

1. Thread.Join

ミッチの答えと同じです。しかしこれはあなたのUIスレッドをブロックするでしょう、しかしあなたはあなたのために組み込まれたタイムアウトを得ます。


2. WaitHandleを使う

jristaが示唆しているように、ManualResetEventWaitHandleです。

注意しなければならないことは、MTAスレッドが必要なため、WaitHandle.WaitAll()がデフォルトでは機能しないということです。 Main()メソッドをMTAThreadでマークすることでこれを回避することができます - しかしこれはあなたのメッセージポンプをブロックし、私が読んだものから推奨されません。


3.イベントを起こす

イベントとマルチスレッドについては このページのJon Skeetによる を参照してください。イベントがifEventName(this,EventArgs.Empty)の間で登録解除される可能性があります。

(うまくいけば、これらはコンパイルされていますが、試していません)

public class Form1 : Form
{
    int _count;

    void ButtonClick(object sender, EventArgs e)
    {
        ThreadWorker worker = new ThreadWorker();
        worker.ThreadDone += HandleThreadDone;

        Thread thread1 = new Thread(worker.Run);
        thread1.Start();

        _count = 1;
    }

    void HandleThreadDone(object sender, EventArgs e)
    {
        // You should get the idea this is just an example
        if (_count == 1)
        {
            ThreadWorker worker = new ThreadWorker();
            worker.ThreadDone += HandleThreadDone;

            Thread thread2 = new Thread(worker.Run);
            thread2.Start();

            _count++;
        }
    }

    class ThreadWorker
    {
        public event EventHandler ThreadDone;

        public void Run()
        {
            // Do a task

            if (ThreadDone != null)
                ThreadDone(this, EventArgs.Empty);
        }
    }
}

4.代理人を使う

public class Form1 : Form
{
    int _count;

    void ButtonClick(object sender, EventArgs e)
    {
        ThreadWorker worker = new ThreadWorker();

        Thread thread1 = new Thread(worker.Run);
        thread1.Start(HandleThreadDone);

        _count = 1;
    }

    void HandleThreadDone()
    {
        // As before - just a simple example
        if (_count == 1)
        {
            ThreadWorker worker = new ThreadWorker();

            Thread thread2 = new Thread(worker.Run);
            thread2.Start(HandleThreadDone);

            _count++;
        }
    }

    class ThreadWorker
    {
        // Switch to your favourite Action<T> or Func<T>
        public void Run(object state)
        {
            // Do a task

            Action completeAction = (Action)state;
            completeAction.Invoke();
        }
    }
}

_countメソッドを使用する場合は、(安全のために)これを使用してインクリメントすることが考えです。

Interlocked.Increment(ref _count)

スレッド通知にデリゲートとイベントを使用することの違いを知りたいと思います。私が知っている唯一の違いは、イベントが同期的に呼び出されることです。


5.代わりに非同期に実行する

この質問 への答えはこの方法であなたのオプションの非常に明確な説明を持っています。


間違ったスレッドでのデリゲート/イベント

イベント/デリゲートのやり方は、あなたのイベントハンドラメソッドがメインUIではなくthread1/thread2にあることを意味します。 threadなので、HandleThreadDoneメソッドの先頭に戻って切り替える必要があります。

// Delegate example
if (InvokeRequired)
{
    Invoke(new Action(HandleThreadDone));
    return;
}
252
Chris S

追加する

t1.Join();    // Wait until thread t1 finishes

起動しても、メインスレッドで実行した場合と同じ結果になるので、それで達成できません。

.NETのスレッドについて理解したい場合は、Joe Albahariの C#でのスレッド 無料の電子書籍を読むことを強くお勧めします。

52
Mitch Wheat

前の2つの答えは素晴らしく、単純なシナリオでもうまくいきます。ただし、スレッドを同期させる方法は他にもあります。以下も動作します。

public void StartTheActions()
{
    ManualResetEvent syncEvent = new ManualResetEvent(false);

    Thread t1 = new Thread(
        () =>
        {
            // Do some work...
            syncEvent.Set();
        }
    );
    t1.Start();

    Thread t2 = new Thread(
        () =>
        {
            syncEvent.WaitOne();

            // Do some work...
        }
    );
    t2.Start();
}

ManualResetEvent は.NETフレームワークが提供しなければならないさまざまな WaitHandle のうちの1つです。それらは、lock()/ Monitor、Thread.Joinなどのような単純だが非常に一般的なツールよりもはるかに豊富なスレッド同期機能を提供することができます。これは、複数の「子」スレッド、同期させるために互いのいくつかの段階に依存する複数の並行プロセスなどを調整します。

30
jrista

.NET 4以降を使用している場合、このサンプルは役に立ちます。

class Program
{
    static void Main(string[] args)
    {
        Task task1 = Task.Factory.StartNew(() => doStuff());
        Task task2 = Task.Factory.StartNew(() => doStuff());
        Task task3 = Task.Factory.StartNew(() => doStuff());
        Task.WaitAll(task1, task2, task3);
        Console.WriteLine("All threads complete");
    }

    static void doStuff()
    {
        //do stuff here
    }
}

から: https://stackoverflow.com/a/4190969/1676736

22

あなたは Thread.Join() メソッド、またはそのうちの一つ オーバーロード が欲しいのです。

5
Daniel Pryden

私はあなたのメインスレッドにあなたの最初のスレッドにコールバックメソッドを渡してもらうでしょう、そしてそれが完了したら、それはメインスレッドのコールバックメソッドを呼び出すでしょう、そしてそれは2番目のスレッドを起動することができます。これは、JoinまたはWaithandleを待っている間、メインスレッドがハングするのを防ぎます。メソッドをデリゲートとして渡すことは、とにかくC#で学ぶのに便利なことです。

4
Ed Power

他の人を助けるために投稿して、私が思いついたような解決策を探すのにかなりの時間を費やしました。それで私は少し違うアプローチを取りました。上記のカウンターオプションがあります、私はちょうどそれを少し異なって適用しました。私は多数のスレッドをスピンオフしていて、スレッドが開始および停止したときにカウンターをインクリメントし、カウンターをデクリメントしました。それからメインメソッドで、私は一時停止してスレッドが完了するのを待ちたいと思っていました。

while (threadCounter > 0)
{
    Thread.Sleep(500); //Make it pause for half second so that we don’t spin the cpu out of control.
}

私のブログに記載されています。 http://www.adamthings.com/post/2012/07/11/ensure-threads-have-finished-before-method-continues-in-c/

0
Adam

タスクの完了を待っている間にUIの表示を更新できるようにするには、スレッドでIsAliveをテストするwhileループを使用します。

    Thread t = new Thread(() => someMethod(parameters));
    t.Start();
    while (t.IsAlive)
    {
        Thread.Sleep(500);
        Application.DoEvents();
    }

0
Mark Emerson