C# - For 루프 패러독스

For 루프 패러독스



우선 for 루프 패러독스가 무엇입니까?
i 내에서 for 루프의 async Task 값을 사용할 때 발생합니다. 작업은 i 의 마지막 값만 사용하여 실행됩니다. for 루프가 매우 큰 경우 작업 중 일부가 중간 값을 얻을 수도 있습니다.

하지만 이 동작을 보여주는 몇 가지 코드를 살펴보겠습니다.

class Program
{
    static async Task Main(string[] args)
    {
        var tasks = new List<Task>();

        for (var i = 0; i <= 9; i++)
        {
            tasks.Add(Task.Run(() =>
            {
                Console.WriteLine(i);
            }));
        }

        await Task.WhenAll(tasks.ToArray());
    }
}

예상되는 결과는 0에서 9까지의 모든 값을 임의의 순서로 콘솔에 인쇄하는 것입니다. 이러한 값은 태스크이고 순서 지정이 보장되지 않기 때문입니다.

그러나 우리가 실제로 얻는 것을 보자:



흠... 뭔가 잘못되었습니다. 우리는 실제로 전혀 예상하지 못한 숫자 10만 인쇄하고 있습니다...

왜 이런 일이 발생합니까?



이것은 클로저의 일반적인 동작이며, 이 코드 조각은 다음과 같습니다.

tasks.Add(Task.Run(() =>
        {
            Console.WriteLine(i);
        }));

실행 당시 i의 값을 사용하고 있습니다.

Closures close over variables, not over values.



알겠습니다. 하지만 이 문제를 해결하는 방법은 무엇입니까?



수정은 매우 간단합니다. i 값을 새 변수에 할당하기만 하면 됩니다.

class Program
{
    static async Task Main(string[] args)
    {
        var tasks = new List<Task>();

        for (var i = 0; i <= 9; i++)
        {
            // work around for closure behavior
            var j = i;

            tasks.Add(Task.Run(() =>
            {
                // use the new j variable here
                Console.WriteLine(j);
            }));
        }

        await Task.WhenAll(tasks.ToArray());
    }
}

이제 for 루프를 반복할 때마다 새 변수 j를 만듭니다. 각 클로저는 한 번만 할당되고 각 반복에서 i 변수의 올바른 현재 값을 유지하는 다른 j 변수에 대해 닫힙니다.

이제 콘솔에서 예상 결과를 얻습니다!



역사 수업 시작!



이 동작은 foreach 루프를 사용할 때도 발생했지만 C# 팀은 C# 5를 릴리스하고 이 동작을 "수정"할 때 주요 변경 사항을 적용했습니다.

그러나 for loop에 대해서는 그대로 유지하기로 결정했습니다.

더 많은 리소스



C#의 클로저와 클로저를 유리하게 사용하는 방법에 대한 자세한 내용은 Jon SkeetThe Beauty of Closures의 문서에서 확인할 수 있습니다.

이 기사를 읽으면 디버그 시간을 절약할 수 있기를 바랍니다.

사랑을 담아 작성한 글입니다❤️

좋은 웹페이지 즐겨찾기