2

c#async / await機能を使用せずに、ブロックせずに非同期操作をループするための最良の方法は何ですか?

たとえば、forループ内でURLのHTMLのリストを非同期的にダウンロードします。

私はしばらくの間ループに陥り続け、TPLはさらに作業がある場合は自分自身を呼び出し続けます。しかし、もっと良い方法はありますか?

4

1 に答える 1

2

あなたが説明するパターンは悪くはありませんが、それをヘルパーメソッドに抽象化することができます。ForEachAsyncまたはのようなものConvertAllAsync。これにより、コードからループが削除されます。これにより、本質的でない複雑さが最小限に抑えられます。


これがの実装ですForEachAsync

    public static Task ForEachAsync<T>(this TaskFactory factory, IEnumerable<T> items, Func<T, int, Task> getProcessItemTask)
    {
        TaskCompletionSource<object> tcs = new TaskCompletionSource<object>();

        IEnumerator<T> enumerator = items.GetEnumerator();
        int i = 0;

        Action<Task> continuationAction = null;
        continuationAction = ante =>
            {
                if (ante.IsFaulted)
                    tcs.SetException(ante.Exception);
                else if (ante.IsCanceled)
                    tcs.TrySetCanceled();
                else
                    StartNextForEachIteration(factory, tcs, getProcessItemTask, enumerator, ref i, continuationAction);
            };

        StartNextForEachIteration(factory, tcs, getProcessItemTask, enumerator, ref i, continuationAction);

        tcs.Task.ContinueWith(_ => enumerator.Dispose(), TaskContinuationOptions.ExecuteSynchronously);

        return tcs.Task;
    }
    static void StartNextForEachIteration<T>(TaskFactory factory, TaskCompletionSource<object> tcs, Func<T, int, Task> getProcessItemTask, IEnumerator<T> enumerator, ref int i, Action<Task> continuationAction)
    {
        bool moveNext;
        try
        {
            moveNext = enumerator.MoveNext();
        }
        catch (Exception ex)
        {
            tcs.SetException(ex);
            return;
        }

        if (!moveNext)
        {
            tcs.SetResult(null);
            return;
        }

        Task iterationTask = null;
        try
        {
            iterationTask = getProcessItemTask(enumerator.Current, i);
        }
        catch (Exception ex)
        {
            tcs.SetException(ex);
        }

        i++;

        if (iterationTask != null)
            iterationTask.ContinueWith(continuationAction, CancellationToken.None, TaskContinuationOptions.ExecuteSynchronously, factory.Scheduler ?? TaskScheduler.Default);
    }
于 2012-12-16T19:37:33.013 に答える