Search

'동시성'에 해당되는 글 1건

  1. 2010.05.27 Welcome to Parallel C#(2) - 계속 되는 개념 찾기.

Welcome to Parallel C#(2) - 계속 되는 개념 찾기.

C# Parallel Programming 2010. 5. 27. 09:00 Posted by 알 수 없는 사용자

- 개념!!

군 생활을 하다 보면, 개념 없는 사람들을 많이 보게 됩니다. 가끔은 '내가 평생 이런 놈을 다시 볼 수 있을까?'싶은 사람도 보게되죠. 그리고 인간관계란 과연 무엇인지 원점에서 다시 생각해 보게 됩니다. 개념 없는 사람들도 사회에서 담당하는 역할이 있는 셈이죠. 오늘은 병렬 프로그래밍에 필요한 개념들을 같이 채워볼까 합니다. 개념 충만한 사람들은 훠이훠이~ 절로 가시고(저는 종교의 다양성을 존중합니당. 교회로 가실 분은 교회로..), 개념 없는 사람들만 저랑 같이 개념을 채워 보시져. 훗.


- 기본 개념!!!

스레드(Thread)

스레드는 다른 순차적 명령 집합(sequence of instructions)과 동시적으로 실행 가능한 순차적 명령 집합을 이야기 합니다. 예를 들면, 뭔가 작업을 처리하는 스레드가 있고, 그 작업의 현황을 화면에 표시하는 스레드가 있는 것 처럼 말이죠. 두 스레드는 서로 다른 순차적 명령 집합을 가지며, 동시적으로 실행 가능하죠. 그리고 두 개 이상의 스레드를 동시적으로 실행하는 것을 멀티스레딩(multithreading)이라고 합니다. 흔히 이야기 하는 동시성이나 병렬성도 최대한 효율적으로 스레드를 여러개 사용하는 것이므로, 멀티스레드의 범주에 들어간다고 할 수 있겠습니다. 운영체제는 이런 멀티 스레드를 동시적으로 처리하는 것 처럼 보여주기 위해서 시간 쪼개기(time slicing)이라는 기법을 활용합니다. 즉, 사람이 눈치채지 못할 정도로 빠른 시간만큼 한번에 하나씩 스레드를 처리하는 것이죠. 단순하게 생각해서, 음악을 들으면서 비주얼 스튜디오로 코딩을 한다면, 사람이 눈치채지 못할 정도의 속도로, 예를 들면 1/24초 정도의 속도로 한번은 음악재생, 한번은 비주얼 스튜디오, 또 한번은 음악 재생, 또 한번은 비주얼 스튜디오 이런식으로 번갈아 가면서 처리하는 것이죠. 이걸 문맥 교환(context switching)이라고 합니다. <그림1>을 보시죠.

<그림1>본격 OS 스케쥴링 그림.jpg

<그림1>에서 나온 것 처럼, 사용자가 눈치채지 못 할만큼의 속도로 두 작업을 번갈아 가면서 CPU가 처리하도록 OS가 스케쥴을 조절하는 것이죠. 그런데 만약, 스레드가 너무 많아서 제대로 CPU를 점거하지도 못하고, 작업을 계속해서 교체하게 되면 어떻게 될까요? 실제로 CPU가 작업을 처리하는 시간보다, 문맥 교환에 더 많은 시간이 들어가게 되므로, 음악이 벅벅 끊기는 등의 지름유발상황이 생기겠죠. 그럼, 시간 쪼개기를 한번 시뮬레이트 해 볼까영? 어헣.

using System;
using System.Threading.Tasks;

namespace Exam2
{
    class Program
    {
        static void Main(string[] args)
        {
            const int max = 10000;

            //현재 작업중인 스레드외에 추가로 스레드를 생성
            Task task = new Task(() =>
                {
                    for (int count = 0; count < max; count++)
                    {
                        Console.Write("|");
                    }
                });

            //추가 스레드 시작
            task.Start();

            //현재 작업중인 스레드에서도 반복문 시작
            for (int count = 0; count < max; count++)
            {
                Console.Write("-");
            }

            //혹시 현재 스레드가 빨리 끝나더라도,
            //추가 스레드가 끝날 때 까지 기다리기.           
            task.Wait();
        }
    }
}

<코드1>아주 단순한 코드

<코드1>은 아주 단순한 코드입니다. 콘솔 어플리케이션의 실행을 담당하는 스레드 외에, 추가로 스레드를 하나 더 생성해서, 두 스레드에서 똑같은 반복문을 돌면서 서로 다른 문자를 출력하게 하는 거죠. 결과를 볼까요?

-|---||||||||||||||||------|||||||||||||||---------------|||||||||||||-----------||||||--------------|||||||||||||--------------|||||||||||||------------||||||||--------------||||||||||||----------||||||||||||||---------|||||||||||||--------|||||||||||||||--------------|||||||||||||||-------------||||||||||||-||||||||||-----------------|||||||||||||||-------------||||||||||||||--|||||||||||||-------|||||||---------------|||||||||||||||----------||||||||||||||---------|||||||||-----------------||||||||||||||-------------|||||||||||||-------------||||||||||-----------------|||||||||||||------------|||||||||||||-----------||||||||------|||||||||||||--------|||||||||||||||-----||||||||||||||-------------||||||||||||----------------|||||||||||||---------------|||||--------------|||||||||||||----||||||||||||||||--------------|||||||||||||||---------||||||||||||-----------|||---------------||||||||||||||||----------------||||||-------------|||||||||----|||||||||||||||||-------------||||||||||||||-||||||||||||||-------------||||||||-------|||||||||||||||--------|--|||---------|||||||||||||--------|||||||||||---|||||||||||||||----------------|||||||||||||||--------------||||-----------------------------------------------------------------------------------------------
계속하려면 아무 키나 누르십시오 . . .

<결과1>평범한 결과

서로 다른 문자가 출력되는 곳이 바로, 시간 쪼개기로 인한 문맥 교환이 일어나는 곳입니다. 정해진 시간만큼 한 스레드가 CPU를 점거하고 작업을 하는 것이죠. 그러면, 약간 방해를 해볼까요? 코드를 다음과 같이 약간 수정해봅니다.

Console.ReadLine();

//추가 스레드 시작
task.Start();

<코드2>수정된 아주 평범한 코드

그리고 이 프로그램이 사용자의 입력을 기다리는 동안, 다음과 같이 프로세스의 우선순위를 낮춰버립니다.

<그림2>프로세스 우선순위 낮추기 ㅋ

그리고 한번 실행해 볼까요? 프로세스의 우선순위가 낮아지면, 실행중인 작업이 우선순위가 높은 다른 프로세스에게 방해를 받을 가능성이 높아지는 거죠.

---------|||||||----||||||||||||||||-----||||||||||||--------||||||||||||||------------|||||||||----------------||||||||||||||------------||||||||||||||-------------|||||||||||----------------|||||||||||||-----------||||||||||||||-------------||||||||||||--------||||||||||||||------------|||||||||||||||-------------|||||||||||||------||||||||||||||-------------||||||||||||||--------------|||||||||||-----------||||---------------|||||||||||||-------------||||||||||||||-------|||||||||||||----|||||||||||||--------------|||||||||||||||-------------|||||||||||||-|||||||||||---------------||||-------------|||||||||||||-------------|||||||||||||---------|----------------|||||||||||||-------------||||||||||||||-----------||||||||||||---------------|||||---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
계속하려면 아무 키나 누르십시오 . . .

<결과2>낮은 우선순위 결과

그런데, <결과2>를 보니, 얼마나 방해를 받은 건지 솔직히 잘 모르겠네요.('-'가 마지막에 막 출력된 건, '|'를 출력하는 스레드가 빨리 작업을 끝내서 입니당) 제 컴이 쿼드에 2.7GHz짜리인데, 아마도 이정도로는 방해가 안되는 거 같습니다. 그래서! 비주얼 스튜디오 2010 20개랑, 음악을 틀고, 바이러스 검사를 돌리고, 인터넷 익스플로러 8도 창을 7개정도 띄워놓고 실험을 해봤습니다.

-||||||||||||||||-------------|---------------|--------------||||||||||----------||||||||||||||-----------||||||||||||--------------|||||||||||---------||||||||-|||||||----|||||||||------------|||||||||||||-------------|||-----------||||||||---------------||||||||||||||-------------||||||-------------||||||||||--------||||||||||||||----|||||||||||||||--------------||||||||||||-----------||||||||||----------------|||||||||||----------|------------||-|||||||||------||-------|||--------------|||||||||||||------------|||||||||||||-------------|||||||||||----|||||||||||-----||||||||||||||---------------||||||||||||||------------|||||||||----------------||||||||||||--------|||||||||||||-------------|||||||||-------|||------------|||||-----|||||||------||||||||||||||||---------------------||||||------|||||||||||||||-------------||||||||||||||-------------||||||||||||--------||||||||||||||||-------------||||||||||--------------|||||||||-------||||------|----||||||-----||||||||||||||-------------|||||||||------------|||||||||||-------||||||||||||||--------------||||||||||||||--------------||||||||||--------||||
|--------------||||||||||||||||-----------|||||||||||||--|||||||||||------||||||||----------------||----||||||||||||||-----||||||||||||||||||||||||||||||||||||||
계속하려면 아무 키나 누르십시오 . . .

<결과3>갖가지 태클을 가한 결과

<결과3>을 보시면, 어느 정도 패턴이 불안정 해진 걸 보실 수 있습니다. 제대로 처리도 못하고, '|'이거 딱 한개 출력하고 문맥 교환이 일어난 부분도 꽤 보이네요.


원자성(Atomicity)

원자성은 뭘까요? 원자성을 만족하는 조건을 보지요. 아래 조건 중에서 하나를 만족하면 원자성을 만족하는 것입니다.

1. 작업을 처리하기 위한 명령어 집합은 다른 작업이 끼어들기 전에 반드시 완료되어야 한다.
2. 작업을 처리 중인 시스템의 상태는, 작업을 처리하기 전의 시스템의 상태로 돌아갈 수 있어야 한다. 즉, 아무 작업도 처리되지 않은 시스템의 상태로 돌아갈 수 있어야 한다.

예를 들면, 은행 계좌에서 돈을 송금하는 걸 생각해보면요. 돈을 송금하기위한 몇가지 단계가 있습니다. 일단 계좌에 돈이 있는지 확인하고, 보낼 계좌가 존재하는지 확인하고, 그리고 돈을 실제로 송금하는 것이죠. 이렇게 송금을 위한 각 단계를 밟는 와중에, 돈을 인출한다던가 하는 작업이 끼어들면 안되겠죠. 그리고 송금 절차가 완료되기 전에는 계좌에 아무런 변화가 없어야 하는 거죠.

C#에서 num++같은 연산을 보면, num의 값을 가져오고, num의 값을 1 증가시키고, num에 증가된 값을 넣는 절차를 밟는데요. ++연산자는 원자성을 만족하지 못합니다. 그래서 num에 증가된 새 값을 넣기 전에, 다른 스레드가 원래 값을 변경 시켜버린다던가 하는 일이 벌어질 수 있죠.


데드락(Deadlock)

데드락은 죽음을 잠근다는 뜻이므로, 죽지 않는다는 뜻입니다. 네, 저를 죽이려고 달려드는 사람들의 모습이 보이네요. 모두들 잠깐만 진정하시고....-_- 이건 서로 볼을 꼬집고서, '니가 먼저 안놓으면, 나도 안놓을 꺼임'하고 외치고 있는 꼬마들을 생각하시면 됩니다. 꼬맹이들은 괜한데서 자존심을 세우죠. 그래서 먼저 놓으면 지는거라고 생각해서 안 놓습니다. 그래서 서로 계속해서 볼을 꼬집고 질질 짜게 되는 거죠. ㅋㅋㅋ

한 스레드는 A라는 자원을 점거하고, B를 가지고 와야 A를 놓아줍니다. 그리고 또 다른 스레드는 B라는 자원을 점거하고, A를 가져와야 B를 놓아준다고 해보죠. 그러면 이 스레드들은 서로 상대방이 가진 자원을 하염없이 기다리는 상황이 됩니다. 죽을 때까지 그 상태로 기다리겠죠. 그래서 이건 데드락입니다. 어헣.


- 정리하면서

위에서 설명드린 원자성이나 데드락은 여러개의 스레드에서 어떤 순서로 명령어를 실행하느냐에 따라서 복잡하게 발생합니당. 이런 프로그램을 짜려면 머리 빠지겠죠. 아이 무셔워라. 그래서 조심해야 하는 것이고, 이런 실수를 최대한 줄여주는 도구가 있어야 하는 것이죠!!

이게 왠 운영체제 수업이냐 하고 반감을 가지시는 분덜도 있으시리라 생각이 됩니다. 모.. 복습했다 생각하시고 어헣. 좋은게 좋은거니깐.. 어헣.


- 참고자료

1. http://www.cafeaulait.org/course/week11/03.html
2. Essential C# 4.0, Mark Michaelis, Addison Wesley