스레드는 하나의 실행 단위로 환경에 따라 여러 스레드를 동시에 실행할 수 있다.
여러 스레드가 하나의 프로세스에서 자원과 메모리를 공유하며 작동한다.
프로세스
- 모든 변수에 대해 공유하지 않는다.
- 서로 다른 식별자를 사용한다.
- 운영체제의 도움으로 통신을 한다.
스레드
- 동일 프로세스의 스레드 간 공유를 한다. 지역 변수는 공유하지 않는다.
- 동일한 프로세스 식별자를 사용한다.
- 전역 변수로 통신이 가능하다.
using namespace std;
#include <iostream> // 입출력 스트림 다룰 때
#include <string> // 문자열 다룰 때
#include <thread> // 스레드 사용
void sum_thread()
{
int sum = 0;
for (int i = 0; i <= 1000; i++)
sum += i;
cout << "sum_thread() : " << sum << endl;
}
int main()
{
// 스레드를 생성할 때 함수를 지정하면 새로운 스레드가 시작되면서 그 스레드가 함수를 실행함
thread sum(sum_thread); // 스레드 생성하고 생성된 스레드가 sum_thread 함수 실행
cout << "main() : start" << endl;
// 생략 시 'abort() has been called' 오류 발생 (쓰레드가 완전히 종료되기 전에 전체 프로그램이 종료되거나 객체가 사라질 때 발생)
sum.join(); // 메인 스레드가 sum_thread 스레드의 실행을 기다리도록 함
cout << "main() : end" << endl;
}
join() 생략 시
스레드가 완전히 종료되기 전에 전체 프로그램이 종료되거나 객체가 사라질 때
abort() has been called 오류 발생
#include <iostream>
#include <thread>
#include <mutex>
using namespace std;
mutex m;
class SUM {
public :
// 인스턴스 없이 호출할 수 있는 정적 함수
static void sum_static(int num) {
m.lock();
int sum = 0;
for (int i = num; i <= 100; i++)
sum += i;
cout << "sum_static() : " << sum << endl;
m.unlock();
}
// 클래스의 멤버 함수 (인스턴스 필요)
void sum_func(int num) {
m.lock();
int sum = 0;
for (int i = num; i <= 1000; i++)
sum += i;
cout << "sum_func() : " << sum << endl;
m.unlock();
}
};
int main()
{
// [Class의 멤버 함수를 사용한 Thread 생성]
// &SUM::sum_func은 SUM 클래스의 멤버 함수를 가리키는 멤버 함수 포인터 (멤버 함수는 클래스의 인스턴스가 필요하기 때문에 객체를 생성해야 함)
// SUM()은 클래스의 임시 인스턴스를 생성, 10은 함수에 전달되는 매개변수
// (class명::thread 수행할 함수, class 생성자, 전달 인자)
thread thread1(&SUM::sum_func, SUM(), 10);
// [Class의 정젹(Static) 함수를 사용한 Thread 생성]
// SUM::sum_static은 정적 함수를 가리키는 함수 포인터, 10은 함수에 전달되는 매개변수
// (class명::thread 수행할 함수, 전달 인자)
thread thread2(SUM::sum_static, 10);
// thread1을 먼저 생성했는데 두 함수의 순서가 실행할 때마다 달라짐
// > 멀티스레딩 환경에서 여러 스레드가 동시에 실행되기 때문에 실행 순서가 보장되지 않음
cout << "main() : start" << endl;
thread1.join();
thread2.join();
cout << "main() : end" << endl;
}
❓thread1을 먼정 생성했는데 두 함수의 순서가 실행될 때마다 달라진다.
> 멀티스레딩 환경에서 여러 스레드가 동시에 실행되기 때문에 실행 순서가 보장되지 않음
#include <stdio.h>
#include <iostream>
// thread를 사용하기 위한 라이브러리 (여러 플랫폼에서 코드를 실행하려면 pthread 사용)
#include <thread>
// thread 동기화를 위한 mutex
#include <mutex>
using namespace std;
class Node
{
private:
int data = 0;
public:
void addData(int data)
{
this->data += data;
}
// 값 리턴
int getData()
{
return this->data;
}
};
void thread_function(Node* node)
{
for (int i = 0; i <= 1000; i++)
{
node->addData(i);
}
}
int main()
{
// Node 선언
Node node;
// 쓰레드 선언, node를 파라미터로 넘김
thread _t1(thread_function, &node);
thread _t2(thread_function, &node);
_t1.join(); // t1 쓰레드가 종료될 때까지 메인스레드 대기
_t2.join(); // t2 쓰레드가 종료될 때까지 메인스레드 대기
// 최종 Node의 값은?
cout << "Node - " << node.getData() << endl;
return 0;
}
1부터 1000까지 더한 것을 두 번 처리했으니 1001000이 나와야 하는데 실행할 때마다 값이 다르게 나옴
> 스레드 간 처리를 기다리거나 동기화하는 작업이 없기 때문
#include <stdio.h>
#include <iostream>
// thread를 사용하기 위한 라이브러리 (여러 플랫폼에서 코드를 실행하려면 pthread 사용)
#include <thread>
// thread 동기화를 위한 mutex
#include <mutex>
using namespace std;
class Node
{
private:
int data = 0;
public:
// 가산을 위한 함수
void addData(int data)
{
this->data += data;
}
// 값 리턴
int getData()
{
return this->data;
}
};
// lock을 위한 mutex
mutex _mutex; //한 번에 한 쓰레드에서만 코드를 실행하도록 하는 객체
//따라서, 뮤텍스를 소유한 쓰레드가 .unlock()을 통해 뮤텍스를 반환할 때까지 기다려야 함
//unlock()을 하지 않는다면 프로그램을 강제 종료 해야 하기 때문에 취득한 뮤텍스는 사용이 끝나면 반드시 반환해야 함
void thread_function(Node* node)
{
for (int i = 0; i <= 1000; i++)
{
// _mutex가 다른 쓰레드에서 lock 걸려있으면 대기하고 걸려있지 않으면 lock 걸고 다음 스텝으로 이동
_mutex.lock();
// node 클래스를 가산
node->addData(i);
// _mutex에 lock 해제
_mutex.unlock();
}
}
// 힙영역에서 사용하게 되면 join뒤에 쓰레드 종료한 후 delete 해줘야 함
int main()
{
// Node 선언
Node node;
// 쓰레드 선언, node를 파라미터로 넘김
thread _t1(thread_function, &node);
thread _t2(thread_function, &node);
_t1.join(); // t1 쓰레드가 종료될 때까지 메인스레드 대기
_t2.join(); // t2 쓰레드가 종료될 때까지 메인스레드 대기
// 최종 Node의 값은?
cout << "Node - " << node.getData() << endl;
return 0;
}
mutex를 이용해 한 번에 한 스레드만 실행하도록 하여 정상적으로 출력됨
출처
https://codedatasotrage.tistory.com/46
https://nowonbun.tistory.com/732
https://zidarn87.tistory.com/574