我有一个简单的功能如下:
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
,它必须返回一个Task<A>
给我。为此,它可能使用也可能不使用参数(类型Func<A, Task<B>
)。如果是这样,我们的回调将被调用并设置结果aa
,然后aa.Task
将完成。否则,结果a
将不依赖于它的参数,所以我们简单地返回它的值。在任何情况下,要么aa.Task
或结果a
将完成,因此它永远不应该阻塞,除非 a 不使用它的参数和块,或者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
}
这里的问题是,这两个任务一旦确定结果aa.Task
就tt
必须清理掉WhenAny
,否则恐怕会出现挂任务泄漏。我不知道该怎么做,有人可以提出一些建议吗?或者这实际上不是问题,C# 会为我做这件事?
PS 这个名字Peirce
来源于((A->B)->A)->A
命题逻辑中著名的“皮尔士定律”( )。
更新:重点不是“处理”任务,而是阻止它们运行。我已经测试过,当我将“主”逻辑放在 1000 个循环中时,它运行缓慢(大约 1 个循环/秒),并创建了很多线程,所以这是一个需要解决的问题。