Parallel Programming (Thread)

2022. 2. 14. 00:00CSharp/Advance

반응형

스레딩을 통해 코드의 병렬 실행을 지원한다.

스레드는 다른 스레드와 동시에 실행할 수 있는 독립적인 실행 경로이다.

 

기본사용법

아래 내용을 실행하면 processor 에 따라 다르겠지만 중간 중간에 11112222211111 같은 형태의 output 이 된다. 

즉  Thread(Write).Start 와 Basic() 내의 for(..) 문이 다른 Thread 상에서 동시에 진행되는 것이다.

public void Basic()
{
    Thread t = new Thread(Write);          
    t.Start();                                         
    for (int i = 0; i < 1000; i++) Console.Write("1");
}

private void Write()
{
    for (int i = 0; i < 1000; i++) Console.Write("2");
}

t.Start() 에 F9  를 통해 break point 를 걸고

Write() 안의 for 문에도 F9 를 통해 break point 를 걸어보자

그 후 F5 를 통해 실행하고 (Debugging 실행) 

Debug-->Windows-->Threads (C++ 환경기준 Ctrl+Alt+H) 를 

실행해보자 아래와 같은 화면이 추가된다. 

t.Start() 의 break point 에서 멈춘상태

현재 Basic() 함수내의 내용은  11244 (1) 의 Main Thread (프로그램의  논리가 흐르는 주 thread ) 에서 실행된다. 

Write() 함수 안의 for 문의 break point 에서 멈춘상태

Write() 함수 내의 내용은 Thread 에 의해 27288 (10) 의 새로운 Worker Thread <No Name> 가 생성된다.

즉 둘은 거의 동시(cpu core 가 여러개라고 가정할때) 에 실행되는 것을 알수 있다.

**실행할때마다 ID 는 변합니다.**

 

아래 이미지와 같다.  다른점은 우리는 x, y 라는 값 대신에 1,2 라는 값을 사용했다.

출처 : http://www.albahari.com/threading/

 

동기화

아래와 같이 int i 라는 변수를 공유하고 있는 Thread 코드가 있다고 하자

private int i = 0;
public void Basic()
{
    Thread t = new Thread(Worker);          
    t.Start();
    Worker();
}

private void Worker()
{
    for (; i < 1000; i++) Console.Write($"{i} ");
}
//output
0 0 2 3 4 5 6 7 8 9 10 1.....

output 을 확인해보면 i 의 값이 순서대로 진행되지 않고 ramdom 하게 변경되는걸 볼수 있다.

이것은 한 Thread 가 i 에 접근하여 i 값을 변경 하려는 동안 

다른 Thread 가 i 에 접근하여 i 값을 변경 하려는 작업이 동시에 일어나서 생기는 일이다.

이럴경우는 lock 을 통해 i 값을 동기화 해야 한다. 

 

다음과 같이 코드를 수정하면 정상적으로 처리되게 된다.

private int i = 0;
private static readonly object locker = new object();
public void Basic()
{
    Thread t = new Thread(Worker);          
    t.Start();
    Worker();
}

private void Worker()
{
    lock (locker)
    {
        for (; i < 1000; i++) Console.Write($"{i} ");
    }
}

Barrier

여러 단계로 구성된 알고리즘에서 사용 하는 쓰레드 동기화 객체

 

private Barrier _barrier = new Barrier(5, barrier => Console.WriteLine());
public void Barrier()
{
    for (int i = 0; i < 5; i++)
    {
        int j = i;
        new Thread(() => Count(j)).Start();
    }
}

/// <summary>
/// barrier 은 생성된 모든 Thread 가 SignalAndWait() 한 부분까지
/// 처리 하기 전까지 다음 라인으로 진행되지 않는다.
/// </summary>
/// <param name="i"></param>
private void Count(int i )
{
    Console.WriteLine(i + ": get from Database ");
    _barrier.SignalAndWait();
    Console.WriteLine(i + ": send to WebServer ");
    _barrier.SignalAndWait();
    Console.WriteLine(i + ": delete from Database ");
    _barrier.SignalAndWait();   
}
// output
1: get from Database
0: get from Database
2: get from Database
3: get from Database
4: get from Database

4: send to WebServer
1: send to WebServer
0: send to WebServer
2: send to WebServer
3: send to WebServer

0: delete from Database
3: delete from Database
4: delete from Database
1: delete from Database
2: delete from Database

 

관련영상

https://youtu.be/Qr7gvQAIi64

 

반응형