0

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.

Dr.Bob
  • 98
  • 1
  • 5
  • 3
    What's wrong with Task.Run? Wasn't it made for running CPU-bound methods? – Clemens Jan 31 '20 at 09:24
  • 1
    You should change the title and the first half of the question to ask/explain what you really want. TaskCompletionSource is a tool, a specialized one. What are you trying to do though, and why don't you use `await` or `Task.Run` ? – Panagiotis Kanavos Jan 31 '20 at 09:24
  • 3
    How is code in B going to progress from the `await` to `tcs.SetResult`? It's going to be stuck at the `await`. – GSerg Jan 31 '20 at 09:25
  • 1
    `await tcs.Task;` doesn't unfreeze anything. `await` doesn't unfreeze, it *awaits* an already asynchronous operation to complete without blocking the thread. If that operation (or tcs in this case) never completes, `await` won't return – Panagiotis Kanavos Jan 31 '20 at 09:26
  • 1
    In `await httpClient.GetStringAsync()`, `GetStringAsync()` is an asynchronous operation, and `await` awaits it without blocking the thread, resuming execution on that thread when it completes. It's not `await` that makes `GetStringAsync` an asynchronous method – Panagiotis Kanavos Jan 31 '20 at 09:28
  • My question does not seem to be a good one. I am sorry I have much to learn. – Dr.Bob Jan 31 '20 at 10:02

1 Answers1

1

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.

John Wu
  • 50,556
  • 8
  • 44
  • 80
  • you are right i could use Task.Run(). It woudl be fine to call Task.Run(). And I should have written tcs.SetResult(true); before await. – Dr.Bob Jan 31 '20 at 09:56