Асинхронный WCF-запрос Последний шаг
Я пытаюсь узнать, как реализовать асинхронный шаблон для одновременного запроса нескольких служб wcf, но не знаю, как проверить, что все одновременные вызовы завершены. У меня есть класс, который выполняет асинхронные операции, а затем добавляет в список по мере выполнения операций:
public static class ODataAsync
{
static DataServiceContext ServiceContext;
static List<DynamicEntity> Results = new List<DynamicEntity>();
private static void GetAsync(string serviceUri, NameValueCollection queryOptions, IAuthenticationScheme authenticationScheme)
{
string baseUri;
string entitySet;
string entityKey;
string queryString;
ValidateServiceUri(serviceUri, out baseUri, out entitySet, out entityKey, out queryString);
string resource = !string.IsNullOrEmpty(entityKey) ? entitySet + "(" + entityKey + ")" : entitySet;
DataServiceContext context = new DataServiceContext(new Uri(baseUri));
context.IgnoreMissingProperties = true;
ServiceContext = context;
DataServiceContextHandler handler = new DataServiceContextHandler(authenticationScheme);
handler.HandleGet(context);
DataServiceQuery<EntryProxyObject> query = context.CreateQuery<EntryProxyObject>(resource);
NameValueCollection options = HttpUtility.ParseQueryString(queryString);
options.Add(queryOptions);
foreach (string key in options.AllKeys)
{
query = query.AddQueryOption(key, options[key]);
}
try
{
query.BeginExecute(GetAsyncComplete, query);
}
catch (DataServiceQueryException ex)
{
throw new ApplicationException("An error occurred during query execution.", ex);
}
}
private static void GetAsyncComplete(IAsyncResult result)
{
QueryOperationResponse<EntryProxyObject> response =
((DataServiceQuery<EntryProxyObject>)result).EndExecute(result) as QueryOperationResponse<EntryProxyObject>;
IList<dynamic> list = new List<dynamic>();
foreach (EntryProxyObject proxy in response)
{
DynamicEntity entity = new DynamicEntity(proxy.Properties);
Results.Add(entity);
}
while (response.GetContinuation() != null)
{
Uri uri = response.GetContinuation().NextLinkUri;
response = ServiceContext.Execute<EntryProxyObject>(uri) as QueryOperationResponse<EntryProxyObject>;
foreach (EntryProxyObject proxy in response)
{
DynamicEntity entity = new DynamicEntity(proxy.Properties);
Results.Add(entity);
}
}
}
}
Мои 2 вопроса:
1) Как я могу убедиться, что я получаю результаты списка только после завершения всех одновременных вызовов? Например, если вызов GetAsync() внутри цикла, запускающий несколько параллельных процессов, мне нужно убедиться, что все они завершены, прежде чем извлекать данные из списка результатов.
2) Могу ли я использовать BeginExecute() внутри вызова GetContinuation() и рекурсивно использовать тот же метод GetAsyncComplete(), что и функция обратного вызова? Или это создало бы тонну потоков и фактически замедлило бы вещи.
Спасибо.
1 ответ
Взгляните на статический метод WaitHandle.WaitAll(WaitHandle[] waitHandles). На этой странице есть отличный пример кода.
Как правило, любой класс, реализующий асинхронный шаблон, будет определять методы в форме:
IAsyncResult BeginXXX(AsyncCallback callback, Object state);
Result EndXXX(IAsyncResult asyncResult);
призвание BeginXXX
вызывает метод асинхронно и вызывает EndXXX
заставляет текущий поток ожидать завершения асинхронного метода.
Чтобы сделать несколько асинхронных вызовов, все, что вам нужно сделать, это позвонить BeginXXX
столько раз, сколько необходимо, а затем позвоните WaitHandle.WaitAll(...)
передавая IAsyncResult.AsyncWaitHandle для всех вызовов, которые вы хотите ждать.
Чтобы ответить на ваш второй вопрос, да, вы можете использовать GetAsynComplete
рекурсивно. state
параметр, данный BeginXXX
Вызов используется для идентификации запроса и может быть доступен через IAsyncResult.UserState
свойство - так что вы можете сопоставить запрос с ответом.
Является ли это хорошей вещью, немного более субъективно. Обычно асинхронные вызовы проходят через пул потоков, поэтому их одновременное создание может исчерпать доступные потоки, и после этого новые вызовы помещаются в очередь до тех пор, пока поток не станет доступным. Существуют некоторые издержки при переключении между потоками, но это, вероятно, не будет значительным (если у вас не очень большое количество потоков) по сравнению со временем, затрачиваемым на это синхронно.