背景
前回、C# > Taskに引数を渡し復帰値を取得するでTaskについて学び、Task.AsyncStateの存在を知りました。
そこで今回はそのAsyncStateプロパティの動作について確認することにしました。
ちょっと気になった点 その1
Taskをnewする時にTaskとかRunとかStartNewで書いたら、IDEから「もっとシンプルに書けるよ。<int>はいらないよ。」と言われました。確かに削除してもコンパイルは通りましたが、意味論的に同等なのかよく分かっておりません。
ちょっと気になった点 その2
AsyncStateプロパティの型はobjectです。なので、中身を参照する時は元々の設定時の型にキャストしてやる必要があります。ジェネリックがある時代なのにキャストしなければならないという点がどうも腑に落ちません。stateの型はobjectだからAsyncStateプロパティの型もobjectなんですよね。Tにできない理由があるのでしょうか。T stateみたいな。
サンプル
GitHub TestTask
using System;
using System.Diagnostics;
using System.Threading;
using System.Threading.Tasks;
namespace TestTask
{
class Program
{
static void Main(string[] args)
{
const int MaxCount = 10;
int previous = -1;
int current = 0;
State state = new State() { Count = MaxCount, Current = 0 };
// 呼び出し方法 その1
// Taskのインスタンスを作ってからStartする。
//
//var task = new Task<int>(o => CountUpMethod((State)o), state);
//task.Start();
// 呼び出し方法 その2
// Taskのインスタンスを作ってからRunするが、
// stateはTaskのインスタンスに渡していない為、
// task.AsyncStateがnullになり、NullReferenceExceptionが発生する。
//
//var task = Task.Run(() => CountUpMethod(state));
// 呼び出し方法 その3
// TaskのFactoryのStartNewでインスタンスの作成とスタートを同時に実行する。
// また、stateをTaskのインスタンスに渡している(インスタンス内にstateが保存される)
var task = Task.Factory.StartNew(o => CountUpMethod((State)o), state);
while (current < MaxCount)
{
current = ((State)task.AsyncState).Current;
if(current != previous )
{
Console.Write($"{current} ");
previous = current;
}
# if DEBUG
Debug.WriteLine($"{current}");
# endif
}
Console.WriteLine("\n\nPush any key!");
Console.ReadKey();
}
static int CountUpMethod(State state)
{
int count = 0;
for (int i = 0; i < state.Count; i++)
{
count++;
state.Current = count;
Thread.Sleep(1000); // 1秒
}
return count;
}
}
class State
{
public int Current { get; set; }
public int Count { get; set; }
}
}
実行結果
コンソール
0 1 2 3 4 5 6 7 8 9 10
Push any key!
[出力]ウィンドウ
1
1
1
(省略)
2
2
2
(省略)
9
9
9
(省略)
10