使用TaskCompletionSource与BufferBlock <T>包装事件

alp*_*ler 4 c# task-parallel-library async-await tpl-dataflow taskcompletionsource

Lucian在这里讨论了一种模式(技巧3:在任务返回API中包装事件并等待它们).

我试图在一个经常调用的方法上实现它,看起来像下面的设计代码:

public Task BlackBoxAsync() 
{ 
    var tcs = new TaskCompletionSource<Object>();  // new'ed up every call
    ThreadPool.QueueUserWorkItem(_ => 
    { 
        try 
        { 
            DoSomethingStuff(); 
            tcs.SetResult(null); 
        } 
        catch(Exception exc) { tcs.SetException(exc); } 
    }); 
    return tcs.Task; 
}
Run Code Online (Sandbox Code Playgroud)

我很担心性能,TaskCompletionSource每次通话都是新的(让我们说我每100毫秒调用一次这个方法).

我当时正在考虑使用BufferBlock<T>,认为每次通话都不会重新开始.所以它看起来像:

private readonly BufferBlock<object> signalDone; // dummy class-level variable, new'ed up once in CTOR

public Task BlackBoxAsync() 
{ 

    ThreadPool.QueueUserWorkItem(_ => 
    { 
        try 
        { 
            DoSomethingStuff(); 
            signalDone.Post(null);
        } 
        catch(Exception exc) {  } 
    }); 
    return signalDone.ReceiveAsync(); 
}
Run Code Online (Sandbox Code Playgroud)

调用对象会将其称为:

for (var i=0; i<10000; i++) {
 await BlackBoxAsync().ConfigureAwait(false);
}
Run Code Online (Sandbox Code Playgroud)

有人对使用有任何想法BufferBlock<T>吗?

i3a*_*non 5

无论你使用什么解决方案,如果你await每次调用这个方法都想要一个任务,创建一个新的Task是不可避免的,因为任务不可重复使用.最简单的方法是使用TaskCompletionSource.

所以第一个选择是使用a BufferBlock(不出所料,创建一个新TaskCompletionSourceReceiveAsync)的首选IMO


更重要的是,您的代码似乎只是将工作卸载到ThreadPool并返回表示该工作的任务.你为什么不用简单的Task.Run

public Task BlackBoxAsync() 
{
    return Task.Run(() => DoSomethingStuff());
}
Run Code Online (Sandbox Code Playgroud)