반응형

안녕하세요.

스레드에서 동시에 같은 메서드에 접근하거나 같은 변수값을 변경할 때, 실행 결과가 잘못될 경우가 있습니다.

이럴때는 쓰레드가 동시에 같은 메서드에 접근을 못하게 하거나 같은 변수에 접근을 못하게 막아야합니다.

이렇게 순차적으로 접근시키고, 동시접근을 막아주는게 동기화라고 할 수 있습니다.

다음 코드는 동기화를 하지 않았을 때 문제가 발생합니다.

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
33
34
35
36
37
38
39
40
41
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
 
namespace ConsoleApplication1
{
    class Program
    {
        static int Count = 0;
 
        static void print()
        {
            for (int i = 0; i < 5; i++)
            {
                Count++;
                Console.WriteLine(Count);
                Thread.Sleep(1000);
            }    
        }
        static void Main(string[] args)
        {
            Thread thread1 = new Thread(new ThreadStart(print));
            Thread thread2 = new Thread(new ThreadStart(print));
            Thread thread3 = new Thread(new ThreadStart(print));
            Thread thread4 = new Thread(new ThreadStart(print));
 
            thread1.Start();
            thread2.Start();
            thread3.Start();
            thread4.Start();
 
            thread1.Join();
            thread2.Join();
            thread3.Join();
            thread4.Join();
 
        }
    }
}
cs

4개의 쓰레드를 생성해서 Count 변수에 1씩 5번 더해줘서 총 20을 더하게 됩니다.
근데 출력된 값을 보면 1~20으로 순차적으로 찍히는게 아니라, 다음과 같이 찍히는 경우도 존재합니다.

제대로 연산이 안될 경우가 있으면, 계속 버그가 만들어지게 되므로, 우리는 항상 규칙적으로 코드가 실행되야 합니다.

그래서 동기화를 사용해서 한 쓰레드가 메모리에 접근중이면 다른 쓰레드가 해당 메모리에 접근하지 못하도록 막습니다.

동기화에는 다음과 같은 클래스를 사용합니다.

1. Lock

상호 배제 잠금을 획득하여, 명령문 브록을 실행 후, 잠금을 해제합니다.
잠금을 보유하는 스레드에서 잠금을 다시 해제할 수 있으며, 
다른 스레드에서는 잠금을 획득이 차단되고, 잠금이 해제될때까지 대기하게 됩니다.

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
33
34
35
36
37
38
39
40
41
42
43
44
45
46
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
 
namespace ConsoleApplication1
{
    class Program
    {
        static int Count = 0;
 
        static System.Object lockThis = new System.Object();
 
        static void print()
        {
            lock (lockThis)
            {
                for (int i = 0; i < 5; i++)
                {
                    Count++;
 
                    Console.WriteLine(Count);
                }
            }
        }
        static void Main(string[] args)
        {
            Thread thread1 = new Thread(new ThreadStart(print));
            Thread thread2 = new Thread(new ThreadStart(print));
            Thread thread3 = new Thread(new ThreadStart(print));
            Thread thread4 = new Thread(new ThreadStart(print));
 
            thread1.Start();
            thread2.Start();
            thread3.Start();
            thread4.Start();
 
            thread1.Join();
            thread2.Join();
            thread3.Join();
            thread4.Join();
 
        }
    }
}
cs

2. Monitor

Lock과 같이 해당 블록을 잠금 후 실행하고, 잠금을 해제합니다.

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
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
 
namespace ConsoleApplication1
{
    class Program
    {
        static int Count = 0;
        static Object monitorLock = new System.Object();
 
        static void print()
        {
            Monitor.Enter(monitorLock);
            try
            {
                for (int i = 0; i < 5; i++)
                {
                    Count++;
                    Console.WriteLine(Count);
                    Thread.Sleep(1000);
                }
            }
            finally
            {
                Monitor.Exit(monitorLock);
            }
        }
        static void Main(string[] args)
        {
            Thread thread1 = new Thread(new ThreadStart(print));
            Thread thread2 = new Thread(new ThreadStart(print));
            Thread thread3 = new Thread(new ThreadStart(print));
            Thread thread4 = new Thread(new ThreadStart(print));
 
            thread1.Start();
            thread2.Start();
            thread3.Start();
            thread4.Start();
 
            thread1.Join();
            thread2.Join();
            thread3.Join();
            thread4.Join();
 
        }
    }
}
cs

Monitor 클래스 중에는 Wait()와 Pulse() 메서드가 있습니다.
쓰레드를 Wait() 메서드로 잠시 대기상태로 만들어 준 후 Pulse()로 활성화 시킬 수 있습니다.
보통 메모리 상에 데이터를 가질때까지 대기하고 데이터를 가지게 되면 활성화 시켜서 작업을 진행할 수 있도록 할 수 있습니다.
다음은 Count 필드 값을 순차적으로 증가 시킨 후 다른 스레드에서 감소합니다.

반응형

'개발공부 > C#' 카테고리의 다른 글

[C#] 스레드(Thread)  (0) 2019.11.04
[C#]컬렉션(Collections)  (0) 2019.10.31
C# 인터페이스(interface)  (0) 2019.10.30
[C#] 배열(Array)  (0) 2019.10.29
[C#] String.Format  (0) 2019.10.28

+ Recent posts