I have to add here I am not a practiced questioner on Stackoverflow so I am glad for feedback concerning why my question might not fit here.
Is awaiting a TaskCompletitionSource a bad thing when wrapping a not async call?
Here is my use case:
I have a handler class which calls a function Func<T, Task>
callback when an event occurs. The handler gets called from outside my Application and notifies my UI.
There are two methods A and B which get used as callback. A where an async HTTP Client gets called and B where I do computation. In both cases the await call will unfreeze the UI and then properties get updated.
A:
public async Task A(){
result = await CallHttpClient(...) // unfreeze UI
// ... copy image bytes and update UI (long running not async)
// release bytes in calling method
}
B:
public async Task B(){
var tcs = new TaskCompletionSource<bool>();
await tcs.Task; // unfreeze UI
// ... copy image bytes and update UI (long running not async)
tcs.SetResult(true); // release bytes in calling method
}
My question here, is it a bad practice to use TaskCompletionSource to wrap a not async call?
The Documentation states the following.
If you want to create a task wrapper for an existing asynchronous operation or event, use TaskCompletionSource. https://learn.microsoft.com/en-us/archive/msdn-magazine/2013/march/async-await-best-practices-in-asynchronous-programming
Another possibility is to call Task.Run(), but it feels even worse to me. Not using Async would result in a freezing UI which is not really what I want although it might be the cleanest solution.
-------> Update
As state by others the Task.Run() is perfectly fine here.
I should note that my B: look different B:
public async Task B(...){
var tcs = new TaskCompletionSource<bool>();
// ... duplicate bytes
tcs.SetResult(true); // release bytes in calling method
await tcs.Task; // unfreeze UI
// ... copy image bytes and update UI (long running not async)
}
Find better option with Task.Run() below.
I should also note that when the method leaves the bytes (not shown in the example) are released.
There is no way to do a CPU-bound task in the background without some sort of multithreading.
This code...
var tcs = new TaskCompletionSource<bool>();
await tcs.Task; // unfreeze UI
// ... copy image bytes and update UI (long running not async)
tcs.SetResult(true); // release bytes in calling method
...will block on the await
because SetResult
is not called until after, resulting in a sort of deadlock.
I suppose you could do something nutty like this
var tcs = new TaskCompletionSource<bool>();
Parallel.Invoke
(
() => await tcs.Task,
() => {
// ... copy image bytes and update UI (long running not async)
tcs.SetResult(true); // release bytes in calling method
}
);
But I'm not sure that would work either. The standard way to do this would be
await Task.Run( () => {
// ... copy image bytes and update UI (long running not async)
});
...which is certainly easier to follow, and is what Task.Run()
is was meant for.