Im в настоящее время находится в процессе переноса клиентского приложения на .NET 4.5, чтобы использовать async/await. Приложение является клиентом для службы WCF, которая в настоящее время предлагает только синхронные службы. Теперь мне интересно, , как я должен асинхронно использовать эту синхронную службу?
Я использую канальные фабрики для подключения к службе WCF, используя контракт на обслуживание, который совместно используется как сервером, так и клиентом. Таким образом, я не могу использовать автоматическое создание из VisualStudio или svcutil
для создания асинхронных клиентских прокси.
Я прочитал этот связанный вопрос, в котором говорится о том, следует ли обернуть синхронный вызов на стороне клиента с помощью Task.Run
или продлить договор на обслуживание с помощью вместо асинхронных методов. Ответ предполагает, что наличие "реальных" асинхронных методов, предлагаемых сервером, лучше для производительности клиента, так как ни один поток не должен будет активно ждать завершения служебного вызова. Это имеет для меня большой смысл, и это будет означать, что синхронные вызовы должны быть обернуты на серверной стороне.
С другой стороны, Стивен Туб описывает это вообще в этом сообщении в блоге. Теперь он не упоминает WCF, поэтому я не уверен, что это справедливо для библиотек, которые работают на одной машине, или если это также относится к вещам, которые запускаются удаленно, но где введение асинхронности оказывает фактическое влияние на соединение/перевод.
И в конце концов, поскольку сервер на самом деле не работает асинхронно (и, скорее всего, не будет для другого в то время), некоторые потоки всегда будут ждать: либо на клиенте, либо на сервере. И это также применяется при одновременном использовании сервисов (в настоящее время клиент ждет в фоновом потоке, чтобы пользовательский интерфейс реагировал).
Пример
Чтобы сделать проблему более ясной, я привел пример. Полный проект доступен для скачать здесь.
Сервер предлагает синхронную услугу GetTest
. Это тот, который в настоящее время существует, и где происходит работа - синхронно. Одним из вариантов было бы обернуть это в асинхронном методе, например, используя Task.Run
, и предложить этот метод в качестве дополнительной услуги в контракте (требуя расширения интерфейса контракта).
// currently available, synchronous service
public string GetTest() {
Thread.Sleep(2000);
return "foo";
}
// possible asynchronous wrapper around existing service
public Task<string> GetTestAsync() {
return Task.Run<string>(() => this.GetTest());
}
// ideal asynchronous service; not applicable as work is done synchronously
public async Task<string> GetTestRealAsync() {
await Task.Delay(2000);
return "foo";
}
Теперь, на стороне клиента, эта служба создается с использованием канала factory. Это означает, что у меня есть только доступ к методам, определенным в контракте на обслуживание, и я особенно не имею доступа к асинхронным методам обслуживания, если я не определяю их явно и не реализую.
В зависимости от того, какие методы теперь доступны, у меня есть два варианта:
-
Я могу асинхронно вызывать синхронную службу, обернув вызов:
await Task.Run<string>(() => svc.GetTest());
-
Я могу асинхронно вызывать асинхронную службу напрямую, которая предоставляется сервером:
await svc.GetTestAsync();
Оба работают нормально и не будут блокировать клиента. Оба метода включают ожидание на каком-то конце: Вариант 1 ждет клиента, что эквивалентно тому, что было сделано ранее в фоновом потоке. Вариант 2 ждет на сервере, обернув там синхронный метод.
Каким будет рекомендуемый способ сделать синхронную службу WCF async? Где я должен выполнять упаковку, на клиенте или на сервере? Или есть ли более эффективные варианты для этого, не ожидая чего-либо, т.е. Путем введения "реальной" асинхронности в соединении, например, сгенерированных прокси-серверов?