Posts C# Parallel
Post
Cancel

C# Parallel

Parallel 클래스


  • 반복적인 병렬 처리를 손쉽게 작성할 수 있는 API를 제공한다.

  • ThreadPool 기반으로 작성되어, ThreadPool의 현재 스레드 개수를 차지하며 최소/최대 스레드 개수에 영향을 받는다.

  • ThreadPool의 스레드 뿐만 아니라 호출 스레드도 병렬 처리에 포함된다.

  • 동기적으로 수행된다. (호출 스레드가 병렬 처리의 종료를 자동적으로 대기한다.)


Parallel.For()


  • for문의 형태와 유사하게 병렬처리를 수행할 수 있다.


[1] 기초

1
2
3
4
Parallel.For(0, 100, i =>
{
    Console.WriteLine($"[{i}] {Thread.CurrentThread.ManagedThreadId}");
});
  • 첫 번째 매개변수 : 시작 인덱스
  • 두 번째 매개변수 : 종료 인덱스 + 1
  • 세 번째 매개변수 : 수행할 동작


[2] 수행 결과 확인하기

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
ParallelLoopResult result = 
    Parallel.For(0, 100, (i, state) =>
    {
        if(i > 50)
            state.Stop();

        Console.WriteLine(i);
    });

Console.WriteLine(!result.IsCompleted ? 
    result.LowestBreakIteration == null ?
    "Suspended" :
    $"Completed to {result.LowestBreakIteration.Value}" :
    "Completed"
);

수행 결과는 ParallelLoopResult 타입의 변수로 확인할 수 있다.

루프가 모두 성공적으로 실행되었으면 .IsCompletedtrue,

도중에 중단되었을 경우 false가 된다.


[3] 결과 합산하기

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// 병렬처리 결과를 받아올 변수
int sum = 0;

Parallel.For(0, 101, 

    // Func<T> : 스레드 지역 변수 초깃값 설정
    () => 0,

    // Func<T, ParallelLoopState, T, T> : 루프 동작
    (i, state, local) =>
    {
        local += i;
        return local;
    },

    // Action<T> : 개별 스레드마다 루프 종료 시 동작
    local =>
    {
        Interlocked.Add(ref sum, local);
    }
);

Console.WriteLine(sum);

위와 같은 형태로 작성하여

동작하는 각각의 스레드마다 로컬 변수에 한 번 결과를 합산하고,

루프가 종료되면 스레드 동기화를 이용해

공유 변수에 결과를 저장하는 형태로 병렬 처리의 최종 결과를 합산할 수 있다.


여기서는 단순 정수 계산이므로 Interlocked를 사용했지만,

다른 경우에도 알맞은 형태의 스레드 동기화를 적용하면 된다.


[4] 옵션 설정하기

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
Parallel.For(0, 101,

    // 병렬처리 옵션 설정
    new ParallelOptions
    {
        MaxDegreeOfParallelism = 2
    },

    // Func<T> : 스레드 지역 변수 초깃값 설정
    () => 0,

    // Func<T, ParallelLoopState, T, T> : 루프 동작
    (i, state, local) =>
    {
        local += i;
        return local;
    },

    // Action<T> : 개별 스레드마다 루프 종료 시 동작
    local =>
    {
        Interlocked.Add(ref sum, local);
    }
);

위와 같이 시작, 종료 인덱스 매개변수 뒤에

ParallelOptions 객체로 병렬 처리 옵션을 설정할 수 있다.


  • MaxDegreeOfParallelism : 병렬 처리에 사용될 최대 스레드 개수
  • CancellationToken : 연결할 취소 토큰 구조체
  • TaskScheduler : 사용할 스케줄러 객체


Parallel.ForEach()


  • 배열, 리스트와 같은 컬렉션에 대해 병렬 처리를 수행한다.

  • 정확히는, IEnumerable<T>를 상속하는 모든 타입에 대해 가능하다.


[1] 기초

1
2
3
4
5
6
int[] intArray = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10};

Parallel.ForEach(intArray, item =>
{
    Console.WriteLine(item);
});


[2] 수행 결과 확인하기

1
2
3
4
5
6
7
int[] intArray = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10};

ParallelLoopResult result = 
    Parallel.ForEach(intArray, item =>
    {
        Console.WriteLine(item);
    });
  • Parallel.For()와 동일하다.


[3] 결과 합산하기, 옵션 설정하기

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
int[] intArray = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
int sum = 0;

Parallel.ForEach(
    
    // 대상 컨테이너
    intArray,

    // 병렬 처리 옵션 설정
    new ParallelOptions()
    {
        MaxDegreeOfParallelism = 4
    },

    // 스레드 지역 변수 초깃값 설정
    () => 0,

    // 루프 동작
    (item, state, local) =>
    {
        local += item;
        return local;
    },

    // 루프 종료 시 동작
    local =>
    {
        Interlocked.Add(ref sum, local);
    }
);

Console.WriteLine(sum);
  • 역시 Parallel.For()와 유사하다.


Parallel.Invoke()


[1] 기초

1
2
3
4
5
6
7
8
9
10
11
12
13
14
private static void InvokeBody()
{
    Thread.Sleep(1000);
    Console.WriteLine($"Invoke : {Thread.CurrentThread.ManagedThreadId}");
}

private static void InvokeTest()
{
    Parallel.Invoke(
        InvokeBody,
        InvokeBody,
        InvokeBody
    );
}
  • Action 타입의 매개변수들을 원하는 개수만큼 넣어 병렬적으로 실행시킬 수 있다.

  • 역시 따로 .Wait() 같은 메소드를 호출할 필요 없이 호출 스레드가 알아서 종료까지 대기한다.


[2] 옵션 설정

1
2
3
4
5
6
7
8
9
10
11
Parallel.Invoke(

    new ParallelOptions
    {
        MaxDegreeOfParallelism = 5
    },

    InvokeBody,
    InvokeBody,
    InvokeBody
);
  • 첫 번째 매개변수로 ParallelOptions 객체를 넣어 옵션을 설정할 수 있다.


References


This post is licensed under CC BY 4.0 by the author.