Мне было интересно, является ли использование TaskCompletitionSource плохим выбором

Должен добавить, что я не являюсь опытным задавателем вопросов в Stackoverflow, поэтому буду рад отзывам о том, почему мой вопрос не подходит сюда.

Является ли ожидание TaskCompletitionSource плохой вещью при переносе неасинхронного вызова?

Вот мой вариант использования:

У меня есть класс обработчика, который вызывает функцию обратного вызова Func<T, Task> при возникновении события. Обработчик вызывается извне моего приложения и уведомляет мой пользовательский интерфейс. Есть два метода A и B, которые используются в качестве обратного вызова. A, где вызывается асинхронный HTTP-клиент, и B, где я выполняю вычисления. В обоих случаях вызов await разморозит пользовательский интерфейс, а затем обновятся свойства.

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
}

Мой вопрос здесь, это плохая практика использовать TaskCompletionSource для переноса не асинхронного вызова?

В документации указано следующее.

Если вы хотите создать оболочку задачи для существующей асинхронной операции или события, используйте TaskCompletionSource. https://docs.microsoft.com/en-us/archive/msdn-magazine/2013/march/async-await-best-practices-in-asynchronous-programming

Другая возможность — вызвать Task.Run(), но мне это кажется еще хуже. Неиспользование Async приведет к зависанию пользовательского интерфейса, что на самом деле не то, что мне нужно, хотя это может быть самое чистое решение.

-------> Обновить

Как утверждают другие, Task.Run() здесь прекрасно работает.

Я должен отметить, что мой B: выглядит иначе 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)
}

Найдите лучший вариант с Task.Run() ниже.

Я также должен отметить, что когда метод выходит, байты (не показанные в примере) освобождаются.


person Dr.Bob    schedule 31.01.2020    source источник
comment
Что не так с Task.Run? Разве это не было сделано для запуска методов, привязанных к процессору?   -  person Clemens    schedule 31.01.2020
comment
Вы должны изменить заголовок и первую половину вопроса, чтобы задать/объяснить, чего вы действительно хотите. TaskCompletionSource — это инструмент, специализированный. Что вы пытаетесь сделать, и почему вы не используете await или Task.Run ?   -  person Panagiotis Kanavos    schedule 31.01.2020
comment
Как код в B будет развиваться от await до tcs.SetResult? Он застрянет на await.   -  person GSerg    schedule 31.01.2020
comment
await tcs.Task; ничего не размораживает. await не размораживается, он ожидает завершения уже асинхронной операции без блокировки потока. Если эта операция (или tcs в данном случае) никогда не завершится, await не вернется   -  person Panagiotis Kanavos    schedule 31.01.2020
comment
В await httpClient.GetStringAsync() GetStringAsync() является асинхронной операцией, а await ожидает ее, не блокируя поток, возобновляя выполнение в этом потоке после его завершения. Не await делает GetStringAsync асинхронным методом   -  person Panagiotis Kanavos    schedule 31.01.2020
comment
Мой вопрос не кажется хорошим. Мне жаль, что мне нужно многому научиться.   -  person Dr.Bob    schedule 31.01.2020


Ответы (1)


Невозможно выполнить задачу, связанную с ЦП, в фоновом режиме без какой-либо многопоточности.

Этот код...

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

... будет блокироваться на await, потому что SetResult не вызывается до тех пор, пока не возникнет тупиковая ситуация.

Я полагаю, вы могли бы сделать что-то сумасшедшее, как это

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
    }
);

Но я тоже не уверен, что это сработает. Стандартный способ сделать это будет

await Task.Run( () => {
    // ... copy image bytes and update UI (long running not async)
});

... что, безусловно, легче понять, и именно для этого предназначено Task.Run().

person John Wu    schedule 31.01.2020
comment
вы правы, я мог бы использовать Task.Run(). Было бы неплохо вызвать Task.Run(). И я должен был написать tcs.SetResult(true); прежде чем ждать. - person Dr.Bob; 31.01.2020