Programing

루프에서 await를 사용하는 방법

lottogame 2020. 10. 19. 07:39
반응형

루프에서 await를 사용하는 방법


컬렉션에서 일부 작업을 수행하는 비동기 콘솔 앱을 만들려고합니다. async / await를 사용하는 다른 버전의 루프를 병렬로 사용하는 버전이 있습니다. 비동기 / 대기 버전이 병렬 버전과 유사하게 작동 할 것으로 예상했지만 동 기적으로 실행됩니다. 내가 도대체 ​​뭘 잘못하고있는 겁니까?

class Program
{
    static void Main(string[] args)
    {
        var worker = new Worker();
        worker.ParallelInit();
        var t = worker.Init();
        t.Wait();
        Console.ReadKey();
    }
}

public class Worker
{
    public async Task<bool> Init()
    {
        var series = Enumerable.Range(1, 5).ToList();
        foreach (var i in series)
        {
            Console.WriteLine("Starting Process {0}", i);
            var result = await DoWorkAsync(i);
            if (result)
            {
                Console.WriteLine("Ending Process {0}", i);
            }
        }

        return true;
    }

    public async Task<bool> DoWorkAsync(int i)
    {
        Console.WriteLine("working..{0}", i);
        await Task.Delay(1000);
        return true;
    }

    public bool ParallelInit()
    {
        var series = Enumerable.Range(1, 5).ToList();
        Parallel.ForEach(series, i =>
        {
            Console.WriteLine("Starting Process {0}", i);
            DoWorkAsync(i);
            Console.WriteLine("Ending Process {0}", i);
        });
        return true;
    }
}

await키워드를 사용하는 방식 은 병렬이 아닌 루프를 통과 할 때마다 기다리는 것을 C #에 알려줍니다. Tasks 목록을 저장 한 다음 await모두를 사용하여 원하는 작업을 수행하도록 메서드를 다시 작성할 수 있습니다 Task.WhenAll.

public async Task<bool> Init()
{
    var series = Enumerable.Range(1, 5).ToList();
    var tasks = new List<Task<Tuple<int, bool>>>();
    foreach (var i in series)
    {
        Console.WriteLine("Starting Process {0}", i);
        tasks.Add(DoWorkAsync(i));
    }
    foreach (var task in await Task.WhenAll(tasks))
    {
        if (task.Item2)
        {
            Console.WriteLine("Ending Process {0}", task.Item1);
        }
    }
    return true;
}

public async Task<Tuple<int, bool>> DoWorkAsync(int i)
{
    Console.WriteLine("working..{0}", i);
    await Task.Delay(1000);
    return Tuple.Create(i, true);
}

코드는 await다음 반복을 시작하기 전에 각 작업 (사용 )이 완료 될 때까지 기다립니다 .
따라서 병렬 처리가 없습니다.

기존 비동기 작업을 병렬로 실행하려면 필요하지 않습니다 await. Tasks 모음을 가져 와서 Task.WhenAll()모두를 기다리는 작업을 반환하기 위해 호출 하면됩니다.

return Task.WhenAll(list.Select(DoWorkAsync));

public async Task<bool> Init()
{
    var series = Enumerable.Range(1, 5);
    Task.WhenAll(series.Select(i => DoWorkAsync(i)));
    return true;
}

C # 7.0 에서는 튜플의 각 멤버에 의미 체계 이름을 사용할 수 있습니다 . 여기 에 새 구문을 사용한 Tim S. 의 대답이 있습니다.

public async Task<bool> Init()
{
    var series = Enumerable.Range(1, 5).ToList();
    var tasks = new List<Task<(int Index, bool IsDone)>>();
    foreach (var i in series)
    {
        Console.WriteLine("Starting Process {0}", i);
        tasks.Add(DoWorkAsync(i));
    }
    foreach (var task in await Task.WhenAll(tasks))
    {
        if (task.IsDone)
        {
            Console.WriteLine("Ending Process {0}", task.Index);
        }
    }
    return true;
}

public async Task<(int Index, bool IsDone)> DoWorkAsync(int i)
{
    Console.WriteLine("working..{0}", i);
    await Task.Delay(1000);
    return (i, true);
}

때로는 copy+ paste준비 솔루션 만 있으면됩니다. 따라서 이것이 Tim.S 의 전체 솔루션입니다 . :

namespace AsyncSimple
{
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Threading.Tasks;

    class Startup
    {
        static void Main()
        {
            var worker = new Worker();
            worker.ParallelInit();
            var t = worker.Init();
            t.Wait();
            Console.ReadKey();
        }
    }

    public class Worker
    {
        public async Task<bool> Init()
        {
            var series = Enumerable.Range(1, 5).ToList();
            var tasks = new List<Task<Tuple<int, bool>>>();
            foreach (var i in series)
            {
                Console.WriteLine("Starting Process {0}", i);
                tasks.Add(DoWorkAsync(i));
            }
            foreach (var task in await Task.WhenAll(tasks))
            {
                if (task.Item2)
                {
                    Console.WriteLine("Ending Process {0}", task.Item1);
                }
            }
            return true;
        }

        public async Task<Tuple<int, bool>> DoWorkAsync(int i)
        {
            Console.WriteLine("working..{0}", i);
            await Task.Delay(1000);
            return Tuple.Create(i, true);
        }
        public bool ParallelInit()
        {
            var series = Enumerable.Range(1, 5).ToList();
            Parallel.ForEach(series, i =>
            {
                Console.WriteLine("Starting Process {0}", i);
                DoWorkAsync(i);
                Console.WriteLine("Ending Process {0}", i);
            });
            return true;
        }
    }
}

그리고 콘솔의 결과 :

여기에 이미지 설명 입력

참고 URL : https://stackoverflow.com/questions/19431494/how-to-use-await-in-a-loop

반응형