일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
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 |
- 프론트엔드 스쿨
- Server
- BFS
- 서버
- 백트래킹
- 문자열&연산자
- map
- 제로베이스 프론트엔드 스쿨
- 코딩테스트
- socket
- C++
- JavaScript
- c#
- 자바스크립트
- Algorithm
- leetcode
- 메모리 배리어
- 완전탐색
- dfs
- 프로그래머스
- 멀티스레드
- MemoryBarrier
- N과 M(2)
- 백준
- 알고리즘
- 코딩테스트 스터디
- 구현
- 구조체
- React
- 제로베이스
- Today
- Total
Written
메모리 배리어 본문
싱글 스레드, 멀티 스레드 코드 흐름의 차이
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
51
52
53
54
55
56
57
58
59
|
using System;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading.Tasks;
namespace client
{
// 메모리 배리어
// 1) 코드 재배치 억제
// 2) 가시성
class Program
{
static int x = 0;
static int y = 0;
static int r1 = 0;
static int r2 = 0;
static void Thread1()
{
y = 1; //Sotre y
r1 = x; //Load x
Console.WriteLine("스레드 1");
}
static void Thread2()
{
x = 1;
r2 = y;
Console.WriteLine("스레드 2");
}
static void Main(string[] args)
{
int count = 0;
while (true)
{
count++;
x = y = r1 = r2 = 0;
Task t1 = new Task(Thread1);
Task t2 = new Task(Thread2);
t1.Start();
t2.Start();
Task.WaitAll(t1, t2);
if (r1== 0 && r2 == 0)
{
break;
}
}
Console.WriteLine($"{count}번 만에 빠져나왔습니다 ! ");
}
}
}
|
cs |
멀티 스레드 환경에서는 코드의 흐름에 주의하자 !
=> 우리가 싱글 스레드에서 해왔던대로 생각해보면, 위 코드에서 r1 과 r2가 0이되면서 break를 통해 빠져나오는 것은 불가능하다. 그런데 어떻게 빠져나올 수 있었을까? 게다가 count의 값은 실행할 때 마다 바뀐다.
=> 일단 t1, t2로 두개의 스레드를 생성했는데, 두 스레드 중 무엇이 먼저 실행될지는 예측할 수 없다. 실제로 콘솔에 출력을 해보면 알수 있다. 스레드1이 먼저 실행될 때도 있고, 스레드2가 먼저 실행되는 경우도 있다. 그것이 멀티 스레드의 특징.
=> 심지어 y = 1 다음줄에 r1 = x를 하도록 코드를 구현했는데, 실행할 때 두 코드가 서로 독립적이면서 둘의 순서를 바꿔서 실행하는게 더 빠르다고 내부에서 판단하면, 우리가 구현한 순서대로 실행하는게 아니라 둘의 순서를 바꿔서 실행할 수 있다는 놀라운 사실이 존재한다. 그리고 하나의 스레드가 다른 스레드의 실행을 기다려주거나 하지 않기 때문에, r1 = 0 이면서 r2 = 0 이 참이되어 나올 수 있었던 것.
=> 그러면 우리가 구현한 순서대로 실행하게끔 하려면 어떻게 해야할까? Thread.MemoryBarrier(); 의 도움을 받으면 가능하다. Thread.MemoryBarrier()는 임의로 코드가 재배치되는 것을 억제하면서 또 하나 중요한 역할을 하는데 그것은 다른 스레드에게 나의 변동사항을 업데이트하고, 다른 스레드의 변동사항을 읽는 것! -> 이게 무슨뜻이냐면 여러개의 스레드는 각자 스레드마다 고유의 스택영역이 있고, 힙 영역의 데이터들은 스레드끼리 공유하는데 누군가 공유하는 영역의 메모리의 변수의 값을 변경했다면 다른 스레드들이 그 내용을 갱신해야 의도치 않은 오류를 방지할 수 있다는 것!
=> Thread.MemoryBarrier()를 사용한다 하더라도 여전히 어떤 스레드가 먼저 실행될지는 모르기 때문에, 그 점을 잘 고려하여 코드를 구현해야한다. 예를 들면 아래의 코드를보면, 어떤 스레드가 먼저실행됬느냐에 따라 _complete의 값이 다르게 출력된다.
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
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
|
using System;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace client
{
// 메모리 배리어
// 1) 코드 재배치 억제
// 2) 가시성
class Program
{
static int _answer = 0;
static bool _complete = false;
static void A()
{
_answer = 123;
Thread.MemoryBarrier();
_complete = true;
Thread.MemoryBarrier();
}
static void B()
{
Thread.MemoryBarrier();
if (_complete)
{
Thread.MemoryBarrier();
Console.WriteLine(_answer);
}
else
{
Console.WriteLine($"complete is false");
}
}
static void Main(string[] args)
{
int count = 0;
while (true)
{
count++;
_answer = 0;
_complete = false;
Task t1 = new Task(A);
Task t2 = new Task(B);
t2.Start();
t1.Start();
Task.WaitAll(t1, t2);
Console.WriteLine($"{count} 바퀴");
}
}
}
}
|
cs |
'Server > 멀티스레드' 카테고리의 다른 글
SpinLock (0) | 2023.07.21 |
---|---|
DeadLock (0) | 2023.07.21 |
캐시(Cache) 이론 (0) | 2023.07.18 |