Как я могу использовать WaitHandle в ожидании завершения асинхронного вызова?
Рассмотрим этот код:
class Program
{
static void Main(string[] args)
{
Master master = new Master();
master.Execute();
}
}
class TestClass
{
public void Method(string s)
{
Console.WriteLine(s);
Thread.Sleep(5000);
Console.WriteLine("End Method()");
}
}
class Master
{
private readonly TestClass test = new TestClass();
public void Execute()
{
Console.WriteLine("Start main thread..");
Action<String> act = test.Method;
IAsyncResult res = act.BeginInvoke("Start Method()..", x =>
{
Console.WriteLine("Start Callback..");
act.EndInvoke(x);
Console.WriteLine("End Callback");
}, null);
Console.WriteLine("End main thread");
Console.ReadLine();
}
}
У нас есть результат:
Start main thread..
End main thread
Start Method()..
End Method()
Start Callback..
End Callback
Итак, я хочу результат:
Start main thread..
Start Method()..
End Method()
Start Callback..
End Callback
End main thread
Как я могу ждать async
в этом коде? Я проверил статью MSDN "Вызов синхронных методов асинхронно" и обнаружил, что это:
После звонка BeginInvoke
Вы можете сделать следующее:
- Сделайте некоторую работу, а затем позвоните
EndInvoke
заблокировать до завершения вызова. - Получить
WaitHandle
с использованиемIAsyncResultAsyncWaitHandle
свойство, используйте егоWaitOne
метод, чтобы заблокировать выполнение доWaitHandle
сигнализируется, а затем позвонитеEndInvoke
, - Опрос
IAsyncResult
вернулсяBeginInvoke
определить, когда асинхронный вызов завершился, а затем вызватьEndInvoke
, - Передать делегат для метода обратного вызова
BeginInvoke
, Метод выполняется наThreadPool
поток, когда асинхронный вызов завершается. Вызов метода обратного вызоваEndInvoke
,
Я думаю, что лучший вариант для меня это второй. Но как реализовать это? В частности мне интересны перегрузки WaitOne()
( Блокирует текущий поток, пока текущий WaitHandle не получит сигнал). Как правильно это сделать? Я имею в виду общую картину в этом случае.
ОБНОВИТЬ:
Сейчас пользуюсь Task<T>
:
class Program
{
static void Main(string[] args)
{
Master master = new Master();
master.Execute();
}
}
class WebService
{
public int GetResponse(int i)
{
Random rand = new Random();
i = i + rand.Next();
Console.WriteLine("Start GetResponse()");
Thread.Sleep(3000);
Console.WriteLine("End GetResponse()");
return i;
}
public void SomeMethod(List<int> list)
{
//Some work with list
Console.WriteLine("List.Count = {0}", list.Count);
}
}
class Master
{
private readonly WebService webService = new WebService();
public void Execute()
{
Console.WriteLine("Start main thread..");
List<int> listResponse = new List<int>();
for (int i = 0; i < 5; i++)
{
var task = Task<int>.Factory.StartNew(() => webService.GetResponse(1))
.ContinueWith(x =>
{
Console.WriteLine("Start Callback..");
listResponse.Add(x.Result);
Console.WriteLine("End Callback");
});
}
webService.SomeMethod(listResponse);
Console.WriteLine("End main thread..");
Console.ReadLine();
}
}
Основная проблема это SomeMethod()
становится пустым list
,
Результат:
Теперь у меня есть чудовищное решение:(
public void Execute()
{
Console.WriteLine("Start main thread..");
List<int> listResponse = new List<int>();
int count = 0;
for (int i = 0; i < 5; i++)
{
var task = Task<int>.Factory.StartNew(() => webService.GetResponse(1))
.ContinueWith(x =>
{
Console.WriteLine("Start Callback..");
listResponse.Add(x.Result);
Console.WriteLine("End Callback");
count++;
if (count == 5)
{
webService.SomeMethod(listResponse);
}
});
}
Console.WriteLine("End main thread..");
Console.ReadLine();
}
Результат:
Это то, что мне нужно ждать асинхронного вызова. Как я могу использовать Wait
за Task
Вот?
ОБНОВЛЕНИЕ 2:
class Master
{
private readonly WebService webService = new WebService();
public delegate int GetResponseDelegate(int i);
public void Execute()
{
Console.WriteLine("Start main thread..");
GetResponseDelegate act = webService.GetResponse;
List<int> listRequests = new List<int>();
for (int i = 0; i < 5; i++)
{
act.BeginInvoke(1, (result =>
{
int req = act.EndInvoke(result);
listRequests.Add(req);
}), null);
}
webService.SomeMethod(listRequests);
Console.WriteLine("End main thread..");
Console.ReadLine();
}
}
2 ответа
Я полагаю, это то, что вы хотите. Мы начинаем задачи, а затем ждем их завершения, после того, как все будет выполнено, мы получим результаты.
class Program
{
static void Main(string[] args)
{
Master master = new Master();
master.Execute();
}
}
class WebService
{
public int GetResponse(int i)
{
Random rand = new Random();
i = i + rand.Next();
Console.WriteLine("Start GetResponse()");
Thread.Sleep(3000);
Console.WriteLine("End GetResponse()");
return i;
}
public void SomeMethod(List<int> list)
{
//Some work with list
Console.WriteLine("List.Count = {0}", list.Count);
}
}
class Master
{
private readonly WebService webService = new WebService();
public void Execute()
{
Console.WriteLine("Start main thread..");
var taskList = new List<Task<int>>();
for (int i = 0; i < 5; i++)
{
Task<int> task = Task.Factory.StartNew(() => webService.GetResponse(1));
taskList.Add(task);
}
Task<List<int>> continueWhenAll =
Task.Factory.ContinueWhenAll(taskList.ToArray(),
tasks => tasks.Select(task => task.Result).ToList());
webService.SomeMethod(continueWhenAll.Result);
Console.WriteLine("End main thread..");
Console.ReadLine();
}
}
Уродливое решение с BeginIvoke/EndInvoke
public void Execute()
{
Func<int, int> func = webService.GetResponce;
var countdownEvent = new CountdownEvent(5);
var res = new List<int>();
for (int i = 0; i < 5; ++i)
{
func.BeginInvoke(1, ar =>
{
var asyncDelegate = (Func<int, int>)((AsyncResult)ar).AsyncDelegate;
int ii = asyncDelegate.EndInvoke(ar);
res.Add(ii);
((CountdownEvent)((AsyncResult)ar).AsyncState).Signal();
}, countdownEvent);
}
countdownEvent.Wait();
Console.WriteLine(res.Count);
}
Из вашего примера кода не ясно, зачем вам нужен асинхронный вызов. Если вы этого не сделаете, вы можете просто позвонить test.Method();
синхронно.
Предполагая, что вам нужно асинхронное выполнение, не беспокойтесь об устаревшем Delegate.BeginInvoke
вещи. Используйте новый Task
API на основе:
var task = Task.Factory.StartNew(() => test.Method());
Вы можете тогда Wait
на задании или await
это или использовать Task.ContinueWith
(выберите подходящую технику для вашего случая).