15

同期メソッドから非同期メソッドを実行しようとしています。しかし、同期メソッドにいるため、非同期メソッドを待つことはできません。TPL を使用するのはこれが初めてなので、TPL を理解していないに違いありません。

private void GetAllData()
{
    GetData1()
    GetData2()
    GetData3()
}

最初のデータが 2 番目のメソッドに使用されるため、各メソッドは前のメソッドを完了する必要があります。

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
    }

ここで await を使用していますが、エラーが発生します

「await」演算子は、非同期メソッド内でのみ使用できます。このメソッドを「async」修飾子でマークし、戻り値の型を「Task」に変更することを検討してください

ただし、async 修飾子を使用すると、これは非同期操作になります。したがって、私の呼び出しがGetData1await 演算子を使用しない場合、最初の await で GetData2 に移動することはできません。これは私が回避しようとしているものですか? GetData1 を非同期メソッドを呼び出す同期メソッドとして保持することは可能ですか? 非同期メソッドの設計が間違っていますか? ご覧のとおり、私はかなり混乱しています。

これは、C# で同期メソッドから非同期メソッドを呼び出す方法の複製である可能性があります。 ただし、複数のタスクを開始しているため、そこで提供されているソリューションを適用する方法がわかりません。WaitAnyそのタスクに対してもう少し処理を行い、すべてのタスクが終了するのを待ってから、呼び出し元に制御を渡します。

アップデート

以下の回答に基づいて私が行った解決策は次のとおりです。

    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/questions/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;
    }
4

3 に答える 3

9

Task<TResult>.Result(またはTask.Wait()結果がない場合) は に似てawaitいますが、同期操作です。これを使用するように変更する必要がありGetData1()ます。変更する部分は次のとおりです。

Task<Data> dataTask = Task.WhenAny(runningTasks).Result;
runningTasks.Remove(dataTask);
myData = gameTask.Result;
于 2013-09-25T16:42:10.183 に答える
0

以下を呼び出すことができます。

GetData1().Wait();
GetData2().Wait();
GetData3().Wait();
于 2013-09-25T16:36:01.687 に答える