Thread

당근한박스·2024년 1월 18일
0

C++

목록 보기
18/23

Thread ?

스레드는 하나의 실행 단위로 환경에 따라 여러 스레드를 동시에 실행할 수 있다.
여러 스레드가 하나의 프로세스에서 자원과 메모리를 공유하며 작동한다.

프로세스와 스레드 차이

  • 프로세스
    - 모든 변수에 대해 공유하지 않는다.
    - 서로 다른 식별자를 사용한다.
    - 운영체제의 도움으로 통신을 한다.

  • 스레드
    - 동일 프로세스의 스레드 간 공유를 한다. 지역 변수는 공유하지 않는다.
    - 동일한 프로세스 식별자를 사용한다.
    - 전역 변수로 통신이 가능하다.




예제[1]

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 오류 발생



예제[2]

#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을 먼정 생성했는데 두 함수의 순서가 실행될 때마다 달라진다.

> 멀티스레딩 환경에서 여러 스레드가 동시에 실행되기 때문에 실행 순서가 보장되지 않음



예제[3-1]

#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이 나와야 하는데 실행할 때마다 값이 다르게 나옴

> 스레드 간 처리를 기다리거나 동기화하는 작업이 없기 때문


예제[3-2] 뮤텍스 사용

#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

0개의 댓글