There's a pattern discussed by Lucian here ( Tip 3: Wrap events up in Task-returning APIs and await them ).
I am trying to implement it on a frequently called method that looks something like the contrived code below:
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;
}
I am worried about the performance, when TaskCompletionSource
is being new'ed up every call (let's just say I call this method every 100 ms).
I was then thinking of using BufferBlock<T>
instead, thinking that it won't be new'ed up every call. So it would look like:
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();
}
The calling objects would call it like:
for (var i=0; i<10000; i++) {
await BlackBoxAsync().ConfigureAwait(false);
}
Does anybody have any thoughts about using BufferBlock<T>
instead?
No matter what solution you'll go with, if you want to await
a task every time you call this method, creating a new Task
is inevitable because tasks aren't reusable. The simplest way to do that is by using TaskCompletionSource
.
So The first option is preferable IMO to using a BufferBlock
(which, unsurprisingly, creates a new TaskCompletionSource
on ReceiveAsync
)
More to the point, your code seems to simply offload work to the ThreadPool
and return a task representing that work. Why aren't you using a simple Task.Run
?
public Task BlackBoxAsync()
{
return Task.Run(() => DoSomethingStuff());
}