У меня есть следующая задача: необходимо в синхронной манере отправить запрос через шину данных и дождаться ответа, после чего вернуть управление вызывающему коду. А могу делать это асинхронно, давая возможность потоку выполнять другую работу. Количество слоев между вызывающим кодом(бизнес-логика) и диспетчером взаимодействия с шиной данных, к примеру, 5. Если я хочу использовать Async/Await, это значит, что на каждом уровне я должен пронести ключевые слова до первого void метода на самом верху. На каждом уровне теперь таск. Вопрос такой: Если в самом низу я реализую ожидание получения ответа через Task.Wait(а не комплишнСорс к примеру, то есть я буду ожидать ответа, блокируя поток), имеет ли какой-нибудь смысл весь этот синтаксический сахар наверху, ведь поток я все-равно заблокирую? Правильно ли я понимаю, что я сам должен побеспокоиться о том, чтобы мой код в самом низу(взаимодействие с шиной данных, жестким диском и пр..) был неблокирующим? В документации написано, что await не блокирует поток, при этом нет никакой доп. информации о том, что мне самому нужно делать соответствующую реализацию и беспокоиться об этом.
Использовать асинхронность имеет смысл в тех случаях, когда задача по своей природе - асинхронна. В таких случаях вам в любом случае придется обеспечивать синхронизацию с остальным кодом, и механизм задач тут - довольно простой вариант.
Если же на самом низком уровне все синхронно - то нет никакого смысла делать все асинхронным.
Иными словами, пляшите от кода низкого уровня, как вам удобнее его писать. А на верхнем уровне просто используйте то, что написано на нижнем. Ну и не забывайте про требования к быстродействию - в том случае когда нижний уровень предоставляет несколько вариантов API.
Рассмотрим ваш пример с шиной. Шина данных (если вы имеете в виду интеграционную шину) может работать несколькими способами. Самый простой из них - синхронные запросы по некоторому протоколу - вы просто делаете запрос и получаете ответ. Если это происходит по HTTP или TCP - то в стандартной библиотеке есть оба варианта API - синхронной и асинхронный. Нужно выбирать тот, который отвечает требованиям по быстродействию. Если у вас одновременно идут сотни или тысячи исходящих запросов - нужно использовать асинхронный вариант (и, конечно же, никаких блокировок на верхнем уровне!). Если вы делаете все запросы последовательно - то синхронного варианта вполне достаточно.
Но у вас упомянут какой-то диспетчер взаимодействия с шиной, значит все не так просто. Значит, шина возвращает ответы на запросы асинхронно и вперемешку, и вам необходимо их прослушивать в отдельном потоке. В таком случае у задачи очевидно асинхронная природа - и вам в любом случае нужна синхронизация. И TaskCompletionSource
дает ее близким к оптимальному способом; странно его не использовать:
public Task<Response> SendRequestAsync (Request req)
{
var tcs = new TaskCompletionSource<Response>(TaskCreationOptions.RunContinuationsAsynchronously);
lock(requests) requests.Add(request.Id, tcs);
// ...
return tcs.Task;
}
public Response SendRequest (Request req) => SendRequestAsync(req).Result;
private void OnResponse(Response resp)
{
lock(requests)
{
TaskCompletionSource<Response> tcs;
if (requests.TryGetValue(resp.RequestId, out tcs))
{
requests.Remove(resp.RequestId);
tcs.SetResult(resp);
}
}
}
И нет никаких проблем с синхронным ожиданием через .Wait()
или .Result
если это необходимо (главное - следите в каком потоке вы это делаете).
Кофе для программистов: как напиток влияет на продуктивность кодеров?
Рекламные вывески: как привлечь внимание и увеличить продажи
В общем, есть 2 строки, формат которых нужно проанализировать на похожесть
Недавно сталкнулся с задачей, в одном элементе которой требовалось преобразование