Task クラスの操作方法を理解しようとしています。これまではいつも通常の Thread クラスを使っていましたが、非同期プログラミングのすべてを把握しようとしています...
例として、すべてのコードを含むメインの Winforms アプリケーションを作成しました。私の問題に関連するコードは次のとおりです。
//Relevant delegates
public delegate void MethodAction(int num);
public delegate void MethodConversion();
public delegate void OnCompletionAction(string completiontext);
//Button user presses
private void button4_Click(object sender, EventArgs e)
{
richTextBox1.Clear();
sw.Reset();
sw.Start();
Sync.RunAsync3(calcSim);
}
//The method that simulates a calculation by adding a sleep
//the input param threadlength is just to allow threads to take longer than others
//since I'm multithreading, I have to invoke the writing code on the windows RichTextbox control
private void calcSim(int threadlength)
{
string threadname = Thread.CurrentThread.Name;
for (int i = 0; i < 10; i++) //Thread calc should take 3s
{
Thread.Sleep(300 + threadlength);
richTextBox1.Invoke((MethodConversion)(() =>
{
richTextBox1.AppendText(string.Format("Thread: {0}\tVersion: {1}\n", threadname, (i + 1).ToString()));
}));
}
}
//Class that contains the different processing methods
public static class Sync
{
public static event OnCompletionAction OnProcCompletion;
public static void RunAsync3(MethodAction doM)
{
Task[] t = new Task[4];
for(int i = 0; i < 4; i++)
{
t[i] = Task.Factory.StartNew((Action)(() => { doM(50 * i); }));
}
Task.WaitAll(t);
if (OnProcCompletion != null) OnProcCompletion("RunSync method finished");
}
}
問題は Task.WaitAll(t) 内にあります... 何らかの理由で、私にはわかりませんが、その行で完全にブロックされ、もう応答しません。その行を省略すると、フォームはリアルタイムで更新され、実行には約 3 秒かかります。
私の質問は、Task.WaitAll() が UI スレッドを 3 秒間ブロックしてから解放し、残りのコードを実行できるようにしないのはなぜですか?
しばらくの間(すべてのスレッドが計算されるまで)UIをブロックする必要があることはわかっていますが、アプリ全体を際限なくブロックします。それは永遠に待っているようですか?
編集
WaitAll の代わりに WhenAll を使用することをお勧めします。RunAsync3 を次のように書き直しました。
public static void RunAsync3(MethodAction doM)
{
Task[] t = new Task[4];
for(int i = 0; i < 4; i++)
{
t[i] = Task.Factory.StartNew((Action)(() => { doM(50 * i); }));
}
//Task.WaitAll(t); -> deadlock
Task.WaitAll(new Task [] { Task.WhenAll(t) });
if (OnProcCompletion != null) OnProcCompletion("RunSync method finished");
}
しかし、これはまだ行き詰まっています...?WhenAll を間違って使用している可能性がありますか?
編集2
私が UI スレッドをブロックしていると主張する誰もが正しかったので、私はこれを別の方法で試すことにしました: UI スレッド内の呼び出しスレッドとして新しいスレッドを実行することによって (UI スレッドではなく私のスレッドでブロックが発生するように)。これは機能しますが、明らかにこれを行うための最良の方法ではありません!
private void button4_Click(object sender, EventArgs e)
{
Thread t = new Thread(new ThreadStart(() =>
{
richTextBox1.Invoke((MethodConversion)(() => richTextBox1.Clear()));
sw.Reset();
sw.Start();
Sync.RunAsync3(calcSim);
}));
t.Start();
}
public static void RunAsync3(MethodAction doM)
{
Task[] t = new Task[4];
for(int i = 0; i < 4; i++)
{
t[i] = Task.Factory.StartNew((Action)(() => { doM(50 * i); }));
}
Task.WaitAll(t);
//Task.WaitAll(new Task [] { Task.WhenAll(t) });
if (OnProcCompletion != null) OnProcCompletion("RunSync method finished");
}