2020년 7월 1일 수요일

C++ Thread

이 글은 지극히 써먹을려고 적은 것이지 기술적인 것이나 문법적인 면을 신경쓴 것이 아니다.

1. thread 기본

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

#include "pch.h"

#include <iostream>

#include <Windows.h>

#include <string>

#include <vector>

#include <queue>

#include <mutex>

#include <functional>

long Jobs = 100;

class Task

{

public:

Task(int num) : num(num ) {};

void DoJob()

{

while (Jobs > 0)

{

Jobs--;

std::cout << num << "'s cur jobs is " << Jobs << std::endl;

}

}

int num;

};

int main()

{

Task task = Task(10);

auto thread = std::thread(&Task::DoJob, &task);

while (Jobs > 0)

{

Jobs--;

std::cout << "main's cur jobs is " << Jobs << std::endl;

}

//thread.join();

return 0;

}

위 함수의 결과값이다.

std::thread 는 함수와 그 함수의 인자를 받는다. 그리고 std::thread 객체를 생성하는 순간 스레드 하나가 나와서 미친듯이 돌기 시작한다.

std::thread 객체의 join 은 대개 주 스레드에 적어주는데, 그 역할은 std::thread 객체가 만든 thread 가 끝날 때 까지 기다리는 것이다. 이게 없으면 주 스레드가 먼저 끝나서 만들어낸 thread 가 돌아갈 데가 없어지는 사태가 바상한다. 디버깅모드에서 비주얼 스튜디오는 자동으로 abort 를 때려줘서 비정상적인 작동을 막아주긴 한다.

2. mutex

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

#include "pch.h"

#include <iostream>

#include <Windows.h>

#include <string>

#include <vector>

#include <queue>

#include <mutex>

#include <functional>

long Jobs = 100;

std::mutex TaskMutex;

class Task

{

public:

Task(int num) : num(num) {};

void DoJob()

{

while (Jobs > 0)

{

std::unique_lock<std::mutex> lock(TaskMutex);

{

Jobs--;

std::cout << num << "'s cur jobs is " << Jobs << std::endl;

}

lock.unlock();

}

}

int num;

};

int main()

{

Task task = Task(10);

auto thread = std::thread(&Task::DoJob, &task);

while (Jobs > 0)

{

std::unique_lock<std::mutex> lock(TaskMutex);

{

Jobs--;

std::cout << "main's cur jobs is " << Jobs << std::endl;

}

lock.unlock();

}

thread.join();

return 0;

}

앞의 코드는 thread 끼리 출력버퍼? 를 공유해서 줄도 마구잡이로 되어 있었다.

이를 해결하기 위한 것이 mutex 이다.

mutex 의 멤버함수로 lock, unlock 을 걸면 그 범위 내에선 lock 건 함수 밖에 사용하지 못한다.

근데 unlock 까먹는 경우가 많아서 스마트포인터 처럼 std::unique_lock 같은 시리즈가 만들어졌다. 이걸 쓰면 굳이 unlock 하지 않아도 소멸자가 호출 될 때 unlock 해준다.

3. conditional variable

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

66

#include "pch.h"

#include <iostream>

#include <Windows.h>

#include <string>

#include <vector>

#include <queue>

#include <mutex>

#include <functional>

long Jobs = 100;

std::mutex TaskMutex;

std::condition_variable conditionVar;

bool Go;

class Task

{

public:

Task(int num) : num(num) {};

void DoJob()

{

while (Jobs > 0)

{

std::unique_lock<std::mutex> lock(TaskMutex);

{

conditionVar.wait(lock, [this]() {return Go; });

if (Jobs % 2 == 1)

{

Jobs--;

std::cout << num << "'s cur jobs is " << Jobs << std::endl;

}

}

lock.unlock();

}

}

int num;

};

int main()

{

Task task = Task(10);

Go = false;

auto thread = std::thread(&Task::DoJob, &task);

while (Jobs > 0)

{

std::unique_lock<std::mutex> lock(TaskMutex);

{

if (Jobs % 2 == 0)

{

Go = false;

Jobs--;

std::cout << "main's cur jobs is " << Jobs << std::endl;

}

else Go = true;

conditionVar.notify_one();

}

lock.unlock();

}

thread.join();

return 0;

}

스레드끼리는 그냥 냅두면 서로 통신하기가 번거롭다. 이를 간단하게 만든 것이 conditional variable 이다.

멤버함수의 wait() 는 std::unique_lock 같은 lock 이랑, bool 값을 return 하는 함수를 받는다. bool 값이 true 면 그 lock 을 진행하고 아니면 멈춘다.

이를 이용해 main 은 짝수를 스레드는 홀수를 처리하게 만들어보았다.

댓글 없음:

댓글 쓰기

List