Рассмотрим этот код:
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();
}
}