Вы выполняете удаленный вызов, и ваш поток должен бездействовать в ожидании результата удаленного вызова. Во время этого ожидания ваш поток может делать полезные вещи, такие как выполнение других удаленных вызовов.
Временами, когда ваш поток бездействует, ожидая завершения других процессов, таких как запись на диск, запрос базы данных или выборка информации из Интернета, обычно это ситуации, когда вы видите асинхронную функцию рядом с неасинхронной функцией: Write
и WriteAsync
, Send
и SendAsync
.
Если на самом глубоком уровне вашего синхронного вызова у вас есть доступ к асинхронной версии вызова, то ваша жизнь будет легкой. Увы, похоже, что у вас нет такой асинхронной версии.
Предложенное вами решение с использованием Task.Run
имеет недостаток, заключающийся в том, что при запуске нового потока (или при его запуске из пула потоков) возникают дополнительные затраты.
Вы можете уменьшить эти накладные расходы, создав объект мастерской. На семинаре выделенный поток (рабочий) или несколько выделенных потоков ожидают в одной точке ввода, чтобы сделать заказ. Потоки выполняют задачу и отправляют результат в точку вывода.
У пользователей семинара есть одна точка доступа (фронт-офис?), Где они отправляют запрос на выполнение чего-либо и ждут результата.
Для этого я использовал System.Threading.Tasks.Dataflow.BufferBlock . Установите пакет Nuget TPL Dataflow.
Вы можете посвятить свою мастерскую приему только работы на GetDataTakingLotsOfTime
; Я сделал свой семинар универсальным: я принимаю каждую работу, которая реализует интерфейс IWork:
interface IWork
{
void DoWork();
}
В WorkShop есть два BufferBlocks
: один для ввода рабочих запросов и один для вывода готовой работы. В мастерской есть поток (или несколько потоков), который ожидает на входе BufferBlock
, пока не придет работа. Выполняет ли Work
, а после завершения отправляет задание на вывод BufferBlock
class WorkShop
{
public WorkShop()
{
this.workRequests = new BufferBlock<IWork>();
this.finishedWork = new BufferBlock<IWork>();
this.frontOffice = new FrontOffice(this.workRequests, this.finishedWork);
}
private readonly BufferBlock<IWork> workRequests;
private readonly BufferBlock<IWork> finishedWork;
private readonly FrontOffice frontOffice;
public FrontOffice {get{return this.frontOffice;} }
public async Task StartWorkingAsync(CancellationToken token)
{
while (await this.workRequests.OutputAvailableAsync(token)
{ // some work request at the input buffer
IWork requestedWork = this.workRequests.ReceiveAsync(token);
requestedWork.DoWork();
this.FinishedWork.Post(requestedWork);
}
// if here: no work expected anymore:
this.FinishedWork.Complete();
}
// function to close the WorkShop
public async Task CloseShopAsync()
{
// signal that no more work is to be expected:
this.WorkRequests.Complete();
// await until the worker has finished his last job for the day:
await this.FinishedWork.Completion();
}
}
TODO: правильная реакция на CancellationToken.CancellationRequested
ТОДО: правильная реакция на исключения, создаваемые работой
TODO: решить, использовать ли несколько потоков для выполнения работы
FrontOffice имеет одну асинхронную функцию, которая принимает работу, отправляет ее в WorkRequests и ожидает завершения работы:
public async Task<IWork> OrderWorkAsync(IWork work, CancellationToken token)
{
await this.WorkRequests.SendAsync(work, token);
IWork finishedWork = await this.FinishedWork.ReceivedAsync(token);
return finishedWork;
}
Итак, ваш процесс создал объект WorkShop и запускает один или несколько потоков, которые запустят StartWorking.
Всякий раз, когда любому потоку (включая ваш основной поток) требуется выполнить некоторую работу в асинхронном режиме:
- Создать объект, который содержит входные параметры и функцию DoWork
- Обратитесь в мастерскую за FrontOffice
- Ожидание OrderWorkAsync
.
class InformationGetter : IWork
{
public int Id {get; set;} // the input Id
public Data FetchedData {get; private set;} // the result from Remoting.GetData(id);
public void DoWork()
{
this.FetchedData = remoting.GetData(this.Id);
}
}
Наконец, версия Async вашего пульта дистанционного управления
async Task<Data> RemoteGetDataAsync(int id)
{
// create the job to get the information:
InformationGetter infoGetter = new InformationGetter() {Id = id};
// go to the front office of the workshop and order to do the job
await this.MyWorkShop.FrontOffice.OrderWorkAsync(infoGetter);
return infoGetter.FetchedData;
}