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();
}
したがって、本質的に、私の質問は、スレッドが他のスレッドの終了を待つ方法です。これを行うための最良の方法は何ですか?
5つの選択肢があります。
ミッチの答えと同じです。しかしこれはあなたのUIスレッドをブロックするでしょう、しかしあなたはあなたのために組み込まれたタイムアウトを得ます。
WaitHandle
を使うjristaが示唆しているように、ManualResetEvent
はWaitHandle
です。
注意しなければならないことは、MTAスレッドが必要なため、WaitHandle.WaitAll()
がデフォルトでは機能しないということです。 Main()
メソッドをMTAThread
でマークすることでこれを回避することができます - しかしこれはあなたのメッセージポンプをブロックし、私が読んだものから推奨されません。
イベントとマルチスレッドについては このページのJon Skeetによる を参照してください。イベントがif
とEventName(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);
}
}
}
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)
スレッド通知にデリゲートとイベントを使用することの違いを知りたいと思います。私が知っている唯一の違いは、イベントが同期的に呼び出されることです。
この質問 への答えはこの方法であなたのオプションの非常に明確な説明を持っています。
イベント/デリゲートのやり方は、あなたのイベントハンドラメソッドがメインUIではなくthread1/thread2にあることを意味します。 threadなので、HandleThreadDoneメソッドの先頭に戻って切り替える必要があります。
// Delegate example
if (InvokeRequired)
{
Invoke(new Action(HandleThreadDone));
return;
}
追加する
t1.Join(); // Wait until thread t1 finishes
起動しても、メインスレッドで実行した場合と同じ結果になるので、それで達成できません。
.NETのスレッドについて理解したい場合は、Joe Albahariの C#でのスレッド 無料の電子書籍を読むことを強くお勧めします。
前の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などのような単純だが非常に一般的なツールよりもはるかに豊富なスレッド同期機能を提供することができます。これは、複数の「子」スレッド、同期させるために互いのいくつかの段階に依存する複数の並行プロセスなどを調整します。
.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
}
}
あなたは Thread.Join()
メソッド、またはそのうちの一つ オーバーロード が欲しいのです。
私はあなたのメインスレッドにあなたの最初のスレッドにコールバックメソッドを渡してもらうでしょう、そしてそれが完了したら、それはメインスレッドのコールバックメソッドを呼び出すでしょう、そしてそれは2番目のスレッドを起動することができます。これは、JoinまたはWaithandleを待っている間、メインスレッドがハングするのを防ぎます。メソッドをデリゲートとして渡すことは、とにかくC#で学ぶのに便利なことです。
他の人を助けるために投稿して、私が思いついたような解決策を探すのにかなりの時間を費やしました。それで私は少し違うアプローチを取りました。上記のカウンターオプションがあります、私はちょうどそれを少し異なって適用しました。私は多数のスレッドをスピンオフしていて、スレッドが開始および停止したときにカウンターをインクリメントし、カウンターをデクリメントしました。それからメインメソッドで、私は一時停止してスレッドが完了するのを待ちたいと思っていました。
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/
タスクの完了を待っている間にUIの表示を更新できるようにするには、スレッドでIsAliveをテストするwhileループを使用します。
Thread t = new Thread(() => someMethod(parameters));
t.Start();
while (t.IsAlive)
{
Thread.Sleep(500);
Application.DoEvents();
}