[c#] 루프에서 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);
}

task. 내부를foreach 제거 할 수도 있습니다 .

// ...
foreach (var (IsDone, Index) in await Task.WhenAll(tasks))
{
    if (IsDone)
    {
        Console.WriteLine("Ending Process {0}", Index);
    }
}
// ...


답변