次のような単純な機能があります。
static Task<A> Peirce<A, B>(Func<Func<A, Task<B>>, Task<A>> a)
{
var aa = new TaskCompletionSource<A>();
var tt = new Task<A>(() =>
a(b =>
{
aa.SetResult(b);
return new TaskCompletionSource<B>().Task;
}).Result
);
tt.Start();
return Task.WhenAny(aa.Task, tt).Result;
}
アイデアは単純です。 の実装ではa
、 a を返さなければなりTask<A>
ません。この目的のために、( type のFunc<A, Task<B>
) パラメータを使用する場合と使用しない場合があります。その場合、コールバックが呼び出され、 の結果が設定され、aa
完了aa.Task
します。それ以外の場合、 の結果はa
そのパラメーターに依存しないため、単純にその値を返します。いずれの状況でも、aa.Task
または の結果a
は完了するため、 do がそのパラメーターとブロックを使用しない限り、またはブロックによって返されるタスクを使用しない限り、決してブロックされるべきではありませんa
。
上記のコードは機能します。たとえば、
static void Main(string[] args)
{
Func<Func<int, Task<int>>, Task<int>> t = a =>
{
return Task.FromResult(a(20).Result + 10);
};
Console.WriteLine(Peirce(t).Result); // output 20
t = a => Task.FromResult(10);
Console.WriteLine(Peirce(t).Result); // output 10
}
ここでの問題は、2 つのタスクaa.Task
とtt
の結果が決定されたらクリーンアップする必要があることWhenAny
です。そうしないと、ハングしたタスクのリークが発生するのではないかと心配しています。これを行う方法がわかりません。誰か何か提案できますか?それとも、これは実際には問題ではなく、C# で解決してくれるのでしょうか?
PS 名前は、命題論理でPeirce
有名な「パースの法則」( ) に由来します。((A->B)->A)->A
更新:問題のポイントは、タスクを「破棄」することではなく、実行を停止することです。「メイン」ロジックを 1000 ループに入れると、実行速度が遅くなり (約 1 ループ/秒)、多くのスレッドが作成されるため、問題を解決することがテストされました。