Вызов метода асинхронного синхронного метода
Я пытаюсь запустить асинхронные методы из синхронного метода. Но я не могу ждать асинхронного метода, так как я синхронно. Я не должен понимать TPL, поскольку это первый раз, когда я его использую.
private void GetAllData()
{
GetData1()
GetData2()
GetData3()
}
Для каждого метода требуется, чтобы предыдущий метод заканчивался, поскольку данные из первого используются для второго.
Однако внутри каждого метода я хочу запустить несколько операций Task
, чтобы ускорить работу. Затем я хочу подождать, пока все они закончатся.
GetData1 выглядит следующим образом
internal static void GetData1 ()
{
const int CONCURRENCY_LEVEL = 15;
List<Task<Data>> dataTasks = new List<Task<Data>>();
for (int item = 0; item < TotalItems; item++)
{
dataTasks.Add(MyAyncMethod(State[item]));
}
int taskIndex = 0;
//Schedule tasks to concurency level (or all)
List<Task<Data>> runningTasks = new List<Task<Data>>();
while (taskIndex < CONCURRENCY_LEVEL && taskIndex < dataTasks.Count)
{
runningTasks.Add(dataTasks[taskIndex]);
taskIndex++;
}
//Start tasks and wait for them to finish
while (runningTasks.Count > 0)
{
Task<Data> dataTask = await Task.WhenAny(runningTasks);
runningTasks.Remove(dataTask);
myData = await dataTask;
//Schedule next concurrent task
if (taskIndex < dataTasks.Count)
{
runningTasks.Add(dataTasks[taskIndex]);
taskIndex++;
}
}
Task.WaitAll(dataTasks.ToArray()); //This probably isn't necessary
}
Я использую ожидание здесь, но получаю сообщение об ошибке
Оператор "ожидание" может использоваться только в асинхронном методе. Рассматривать маркировка этого метода модификатором "async" и изменение его возврата введите "Задача"
Однако, если я использую модификатор async, это будет асинхронная операция. Поэтому, если мой вызов GetData1
не использует оператор ожидания, он не будет управлять GoData2 при первом ожидании, чего я пытаюсь избежать? Можно ли сохранить GetData1 в качестве синхронного метода, который вызывает асинхронный метод? Я неправильно проектирую асинхронный метод? Как вы можете видеть, я очень смущен.
Это может быть дубликат Как вызвать асинхронный метод из синхронного метода на С#? Однако я не уверен, как применять предоставленные там решения, как я 'm запускать несколько задач, хотите WaitAny
, сделать немного больше обработки для этой задачи, а затем дождаться завершения всех задач до передачи управления обратно вызывающему.
UPDATE
Вот решение, на которое я пошел, основываясь на ответах ниже:
private static List<T> RetrievePageTaskScheduler<T>(
List<T> items,
List<WebPageState> state,
Func<WebPageState, Task<List<T>>> func)
{
int taskIndex = 0;
// Schedule tasks to concurency level (or all)
List<Task<List<T>>> runningTasks = new List<Task<List<T>>>();
while (taskIndex < CONCURRENCY_LEVEL_PER_PROCESSOR * Environment.ProcessorCount
&& taskIndex < state.Count)
{
runningTasks.Add(func(state[taskIndex]));
taskIndex++;
}
// Start tasks and wait for them to finish
while (runningTasks.Count > 0)
{
Task<List<T>> task = Task.WhenAny(runningTasks).Result;
runningTasks.Remove(task);
try
{
items.AddRange(task.Result);
}
catch (AggregateException ex)
{
/* Throwing this exception means that if one task fails
* don't process any more of them */
// https://stackoverflow.com/info/8853693/pattern-for-implementing-sync-methods-in-terms-of-non-parallel-task-translating
System.Runtime.ExceptionServices.ExceptionDispatchInfo.Capture(
ex.Flatten().InnerExceptions.First()).Throw();
}
// Schedule next concurrent task
if (taskIndex < state.Count)
{
runningTasks.Add(func(state[taskIndex]));
taskIndex++;
}
}
return items;
}
Ответы
Ответ 1
Task<TResult>.Result
(или Task.Wait()
, когда нет результата) аналогичен await
, но является синхронной операцией. Вы должны изменить GetData1()
, чтобы использовать это. Здесь часть для изменения:
Task<Data> dataTask = Task.WhenAny(runningTasks).Result;
runningTasks.Remove(dataTask);
myData = gameTask.Result;
Ответ 2
Во-первых, я рекомендую, чтобы ваши "внутренние" задачи не использовали Task.Run
в своей реализации. Вы должны использовать метод async
, который синхронно выполняет привязку к процессору.
Как только ваш MyAsyncMethod
представляет собой метод async
, который обрабатывает некоторую обработку с привязкой к процессору, вы можете обернуть его в Task
и использовать параллельную обработку как таковую:
internal static void GetData1()
{
// Start the tasks
var dataTasks = Enumerable.Range(0, TotalItems)
.Select(item => Task.Run(() => MyAyncMethod(State[item]))).ToList();
// Wait for them all to complete
Task.WaitAll(dataTasks);
}
Ограничение вашего concurrency в исходном коде не будет работать вообще, поэтому я удалил его для простоты. Если вы хотите применить ограничение, вы можете использовать SemaphoreSlim
или TPL Dataflow.
Ответ 3
Вы можете вызвать следующее:
GetData1().Wait();
GetData2().Wait();
GetData3().Wait();