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 newTask
is inevitable because tasks aren't reusable. The simplest way to do that is by usingTaskCompletionSource
.So The first option is preferable IMO to using a
BufferBlock
(which, unsurprisingly, creates a newTaskCompletionSource
onReceiveAsync
)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 simpleTask.Run
?