Как я могу запускать оба этих метода "одновременно" в .NET 4.5?
У меня есть метод, который делает две независимые части логики. Я надеялся, что я смогу запустить их обоих одновременно... и только потом, когда оба этих дочерних метода завершатся.
Я пытался разглядеть синтаксис async/await
, но я просто не понимаю.
Здесь код:
public PewPew SomeMethod(Foo foo)
{
var cats = GetAllTheCats(foo);
var food = GetAllTheFood(foo);
return new PewPew
{
Cats = cats,
Food = food
};
}
private IList<Cat> GetAllTheCats(Foo foo)
{
// Do stuff, like hit the Db, spin around, dance, jump, etc...
// It all takes some time.
return cats;
}
private IList<Food> GetAllTheFood(Foo foo)
{
// Do more stuff, like hit the Db, nom nom noms...
// It all takes some time.
return food;
}
Итак, с этим кодом выше, я хочу сказать: идите и получите всех кошек и продуктов питания в одно и то же время. Как только мы закончим, вернем новый PewPew
.
Я смущен, потому что я не уверен, какие классы выше async
или возвращают Task
и т.д. Все из них? просто две частные? Я также предполагаю, что мне нужно использовать метод Task.WaitAll(tasks)
, но я не уверен, как настроить задачи для запуска в одно и то же время.
Предложения, добрые люди?
Ответы
Ответ 1
Вот что вы можете сделать:
public async Task<PewPew> SomeMethod(Foo foo)
{
// get the stuff on another thread
var cTask = Task.Run(() => GetAllTheCats(foo));
var fTask = Task.Run(() => GetAllTheFood(foo));
var cats = await cTask;
var food = await fTask;
return new PewPew
{
Cats = cats,
Food = food
};
}
public IList<Cat> GetAllTheCats(Foo foo)
{
// Do stuff, like hit the Db, spin around, dance, jump, etc...
// It all takes some time.
return cats;
}
public IList<Food> GetAllTheFood(Foo foo)
{
// Do more stuff, like hit the Db, nom nom noms...
// It all takes some time.
return food;
}
Здесь вам нужно понять две вещи:
1) Что такое diff:
var cats = await cTask;
var food = await fTask;
И это:
Task.WaitAll(new [] {cTask, fTask});
Оба дают вам аналогичный результат в том смысле, что заканчиваются задачи 2 async
, а затем return new PewPew
- однако разница заключается в том, что Task.WaitAll()
будет блокировать текущий поток (если это поток UI, тогда UI замерзнет). вместо этого await
разбивает SomeMethod
на конечный автомат и возвращает из SomeMethod
его вызывающему, когда он встречает ключевое слово await
. Он не будет блокировать поток. Код ниже await
будет запланирован для запуска, когда задача async
завершена.
2) Вы также можете сделать это:
var cats = await Task.Run(() => GetAllTheCats(foo));
var food = await Task.Run(() => GetAllTheFood(foo));
Однако это не будет запускать задачи async
одновременно. Вторая задача начнется после завершения первого. Это связано с тем, что работает ключевое слово await
, надеюсь, что это поможет...
EDIT: как использовать SomeMethod
- где-то в начале дерева вызовов, вы должны использовать свойство Wait()
или Result
- ИЛИ - вы должны await
от async void
.
Как правило, async void
будет обработчиком событий:
public async void OnSomeEvent(object sender, EventArgs ez)
{
Foo f = GetFoo();
PewPew p = await SomeMethod(f);
}
Если нет, используйте свойство Result
.
public Foo2 NonAsyncNonVoidMethod()
{
Foo f = GetFoo();
PewPew p = SomeMethod(f).Result; //But be aware that Result will block thread
return GetFoo2(p);
}
Ответ 2
Самый простой способ сделать это - использовать Parallel.Invoke()
IList<Cat> cats;
IList<Food> food;
Parallel.Invoke
(
() => cats = GetAllTheCats(foo),
() => food = GetAllTheFood(foo)
);
Parallel.Invoke()
будет ждать, пока все методы вернутся, прежде чем он сам вернется.
Дополнительная информация здесь: http://msdn.microsoft.com/en-us/library/dd460705.aspx
Обратите внимание, что Parallel.Invoke()
обрабатывает масштабирование до числа процессоров в вашей системе, но это действительно имеет значение, если вы начинаете больше, чем просто несколько задач.
Ответ 3
Вам не нужно использовать async, если вы не используете метод async или используете более старую версию .NET Framework. Для простоты используйте Задачи:
Task taskA = Task.Factory.StartNew(() => GetAllTheCats(foo));
Task taskB = Task.Factory.StartNew(() => GetAllTheFood(foo));
Task.WaitAll(new [] { taskA, taskB });
// Will continue after both tasks completed
Ответ 4
Вы можете использовать TPL, чтобы ждать нескольких задач во время их работы. См. здесь.
Вот так:
public PewPew SomeMethod(Foo foo) {
IList<Cat> cats = null;
IList<Food> foods = null;
Task[] tasks = new tasks[2] {
Task.Factory.StartNew(() => { cats = GetAllTheCats(foo); }),
Task.Factory.StartNew(() => { food = GetAllTheFood(foo); })
};
Task.WaitAll(tasks);
return new PewPew
{
Cats = cats,
Food = food
};
}
Ответ 5
Добавляя к другим ответам, вы можете сделать что-то вроде:
public PewPew SomeMethod(Foo foo)
{
Task<IList<Cat>> catsTask = GetAllTheCatsAsync(foo);
Task<IList<Food>> foodTask = GetAllTheFoodAsync(foo);
// wait for both tasks to complete
Task.WaitAll(catsTask, foodTask);
return new PewPew
{
Cats = catsTask.Result,
Food = foodTask.Result
};
}
public async Task<IList<Cat>> GetAllTheCatsAsync(Foo foo)
{
await Task.Delay(7000); // wait for a while
return new List<Cat>();
}
public async Task<IList<Food>> GetAllTheFoodAsync(Foo foo)
{
await Task.Delay(5000); // wait for a while
return new List<Food>();
}