主に備忘録的な小ネタシリーズ。
疑問
CancellationToken
ってアチコチで使う。
大体がCancellationTokenSource.Token
で作るんだけど、プロパティだから何回参照しても同じ?
回答
CancellationTokenSource.Token
って見た目はプロパティなんだけど、ソースを見てみたらこんな感じだった。
public CancellationToken Token
{
get
{
ThrowIfDisposed();
return new CancellationToken(this);
}
}
おい、毎回Newしとるやんけ!!!
でも、
CancellationTokenSource tokenSource = new CancellationTokenSource();
CancellationToken token1 = tokenSource.Token;
CancellationToken token2 = tokenSource.Token;
Assert.Equal(token1, token2);
なんてテストを書くとAssertは通る。つまりtoken1 == token2
はtrue
になる。
これは単純にCancellationToken
がIEquatable<CancellationToken>
を継承しているから。
で、何をもって同じと判断しているかというと、元になるCancellationTokenSource
が同じ(Equal()
)であれば「同じ」と見なされている。
蛇足で、GetHashCode()
もしっかりOverrideされていて、元になるCancellationTokenSource
の.GetHashCode()
を引っ張ってくるようになっている。
ついでに、CancellationToken
ってstruct
なのね。知らんかった。
ってことはメソッドの引数にしたりするとstruct
は値型(Value Type)なのでコピー渡しされるってことかね??
Assert.Same()
でCancellationToken
同士の参照を比較しようとしても、「Assert.Same()にValue Typeを入れないでね。」とレコメンドが出る。ま、そうだよね。
もしかするとCLR Runtime側で何か特別扱いしているのかもしれないけど、メソッドの引数にセットしたりするとコピー渡しされるんだろうなー。パフォーマンス原理主義者は怒るんだろうな~。
とか、考えてみる。
Register()は??
ってことは、だ、
メソッドの引数にCancellationToken
渡して渡し元と渡し先で
token.Register(() => { ほげほげ}
とかやるとどうなるんだろう??
ちなみにCancellationToken.Register()
は、そのToken(正確にはTokenSource)でキャンセルを行った後に実行したいActionを登録しておくことができるメソッドです。
……ってことで調べてみました。ソースを。
-
Register()
は戻り値としてCancellationTokenRegistration
というClassのオブジェクトを返す。 -
Register()
の中で、作ったCancellationTokenRegistration
を、Tokenの元のTokenSourceに登録している。(大雑把に言えば、CancellationTokenSource.Register()
へのショートカットですね。どっちにRegister()しても結局同じ。) -
CancellationTokenRegistration
をCancellationTokenSource
やCancellationToken
から.Unregister()
すると、キャンセルされたときにそのCallbackは 実行されなくなる。 - もうひとつ、
CancellationTokenRegistration
自体をDispose()
するとUnregister()
されたのと同じ効果がある。 - キャンセル時のCallbackは
CancellationTokenSource
から実行される。 - キャンセル済みの
CancellationTokenSource
やCancellationToken
に.Register()
すると、Callbackは即時実行される。
なるほどね。