[C++기초] 20. 쓰레딩(Threading) 라이브러리

INDEX

  1. 쓰레드 개체 만들기
  2. 뮤텍스
  3. 조건 변수

쓰레드 개체 만들기

쓰레드 지원 라이브러리

C++ 이전의 멀티 쓰레딩

윈도우 쓰레드와 POSIX 쓰레드

std::thread

std::thread 생성자 1

thread() noexcept;

template<class Function, class.. Args>
explicit thread(Function&& f, Args&&.. args);

std::thread 생성자 2

thread(thread&& other) noexcept;
thread(const thread&) = delete;
std::thread printThread(PrintMessage, 10); // void PrintMessage(int count);
std::thread movedThread(std::move(printThread)); // OK. printThread는 더 이상 쓰레드가 아님
std::thread copiedThread = movedThread; //컴파일 에러

std::thread::join()

void join()

예시 : 쓰레드 개체 만들기

#include <iostream>
#include <string>
#include <thread>
void PrintMessage(const std:;string& message)
{
    std::cout << message << std::endl;
}
int main()
{
    std::thread thread(PrintMessage, "Message from a child thread.");

    PrintMessage("Message from a main thread.");

    thread.join(); // 자식 쓰레드가 끝날 때까지 기다림

    return 0;
}

std::thread::get_id()

std::thread::id get_id() const noexcept;

std::thread thread(PrintMessage); // void PrintMessage() {}
std::thread::id threadID = thread.get_id();

예시 : 쓰레드 ID 구하기

#include <iostream>
#include <sstream>
#include <string>
#include <thread>

void PrintMessage(const std:;string& message)
{
    std::cout << message << std::endl;
}

int main()
{
    std::thread thread(PrintMessage, "Message from a child thread.");

    std::thread::id childThreadID = thread.get_id();
    std::stringstream ss;
    ss << childThreadID;
    std::string childThreadIDStr = ss.str();

    PrintMessage("Waiting the child thread(ID: "+childThreadIDStr+")");

    thread.join(); // 자식 쓰레드가 끝날 때까지 기다림

    return 0;
}

std::thread::detach()

void detach();

std::thread thread(PrintMessage); // void PrintMessage() {}
thread.detach();

예시 : 쓰레드 떼어내기

#include <iostream>
#include <string>
#include <thread>
void PrintMessage(const std:;string& message)
{
    for(int i =0; i<count; ++i)
    {
        std::cout << i + 1 << " : " message << std::endl;
    }
}
int main()
{
    std::thread thread(PrintMessage, "Message from a child thread."),10;
    
    PrintMessage("Waiting the child thread...",1);
    
    thread.detach();

    return 0;
}

Result

image

예시 : 떼어진 쓰레드 join 방지하기

#include <iostream>
#include <string>
#include <thread>
void PrintMessage(const std:;string& message, int count)
{
    for(int i =0; i<count; ++i)
    {
        std::cout << i + 1 << " : " message << std::endl;
    }
}
int main()
{
    std::thread thread(PrintMessage, "Message from a child thread.",10);

    PrintMessage("Waiting the child thread...",1);

    thread.detach();
    // ...
    if (thread.joinable())
    {
        thread.join();
    }
    return 0;
}

예시 : 람다와 쓰레딩

#include <iostream>
#include <string>
#include <thread>
int main()
{
    auto printMessage = [](const std::string& messgae)
    {
        std::cout << message << std::endl;
    };

    std::thread thread(PrintMessage, "Message from a child thread.");

    printMessage("Waiting the child thread...");
    thread.join();

    return 0;
}

std::ref()

template<class T>
std::reference_wrapper<T> ref(T& t) noexcept;
//void Sum(const std::Vector<int>& list, int& result);
std::thread thread(Sum, list, std::ref(result));

예시 : 매개변수 참조로 전달하기

#include <iostream>
#include <string>
#include <thread>
#include <vector>

int main()
{
    std::vector<int> list(100, 1);
    int result = 0;

    std::thread thread([](const std::vector<int>& v, int& result)
                       {
                           for(auto item : v)
                           {
                               result += item;
                           }
                       }, list, std::ref(result));

    thread.join();

    std::cout << "Result:" << result << std::endl;

    return 0;
}

std::this_thread

std::this_thread::sleep_for()

template<class Rep, class Period>
void sleep_for(const std::chrono::duration<Rep, Perio>& sleep_duration);

std::this_thread::sleepfor(std::chrono:seconds(1));

예시 : 쓰레드 실행 잠시 멈추기

#include <iostream>
#include <chrono>
#include <string>
#include <thread>
void PrintCurrentTime() { /* 현재 시간 출력 */}
void PrintMessage(const std::string& message)
{
    std::cout << "Sleep now...";
    PrintCurrentTime();

    std::this_thread::sleep_for(std::chrono::seconds(3));

    std::cout<<message<<" ";
    PrintCurrentTime();
}
int main()
{
    std::thread thread(PrintMessage, "Message from a child thread.");

    PrintMessage("Message from a main thread");

    thread.join();
    return 0;
}

std::this_thread::yield()

void yield() noexcept;

std::this_thread::yield();

예시 : 다른 쓰레드에게 양보하기

#include <iostream>
#include <chrono>
#include <string>
#include <thread>

void PrintMessage(const std::string& message, std::chrono::seconds delay)
{
    auto end = std::chrono::high_resolution_clock::now() + delay;

    while(std::chrono::high_resolution_clock::now() < end)
    {
        std::this_thread::yield();
    }
}
int main()
{
    std::thread thread(PrintMessage, "Message from a child thread.", std::chrono::seconds(3));

    PrintMessage("Message from a main thread", std::chrono::seconds(2));

    thread.join();

    return 0;
}

뮤텍스

뮤텍스

뮤텍스 생성자

std::mutex mutex;

std::mutex::lock()

void lock();

std::mutex mutex;
mutex.lock();

std::mutex::unlock()

void unlock();

std::mutex mutex;

// mutex.lock();
// ...
mutex.unlock();

예시 : 공유자원 잠그기

#include <iostream>
#include <mutex>
#include <string>
#include <thread>

void PrintMessage(const std::string& message)
{
    static std::mutex sMutex;

    sMutex.lock();
    std::cout << message << std::endl;
    sMutex.unlock();
}

int main()
{
    std::thread thread(PrintMessage, "Message from a child thread.");

    PrintMessage("Messge from a main thread");

    thread.join();

    return 0;
}

std::scoped_loc(C++17)

template<class... MutexTypes>
class scoped_lock;

예시 : scoped_lock 1

#include <iostream>
#include <mutex>
#include <string>
#include <thread>
void PrintMessage(const std::string& message)
{
    static std::mutex sMutex;

    std::scoped_lock<std::mutex> lock(sMutex);
    std::cout << message << std::endl;
}
int main()
{
    std::thread thread(PrintMessage, "Message from a child thread.");

    PrintMessage("Messge from a main thread");

    thread.join();

    return 0;
}

예시 : scoped_lock 2

#include <iostream>
#include <mutex>
#include <string>
#include <thread>

void PrintMessage(const std::string& message)
{
    static std::mutex sMutex;

    {
        std::scoped_lock<std::mutex> lock(sMutex);
        std::cout << "Message from thread ID " << std::this_thread::get_id() << std:;endl;
    }
    // 스코프를 벗어나면서 lock이 풀림!!

    {
        std:: scoped_lock<std::mutex> lock(sMutex);
        std::cout << message << std::endl;
    }
}
int main()
{
    std::thread thread(PrintMessage, "Message from a child thread.");

    PrintMessage("Messge from a main thread");

    thread.join();

    return 0;
}

조건 변수

조건 변수

std::condition_variable

std::unique_lock

std::condition_variable::wait

void wait(std::unique_lock<std::mutex>& lock);

예시 : 잘못된 조건변수 사용

#include <iostream>
#include <mutex>
#include <queue>

static std::mutex sQueueLock;
static std::condition_variable sEvent;
static std::queue<int> sQueue;

void Consume()
{
    while(true)
    {
        int val;
        {
            std::unique_lock<std::mutex> lock(sQueueLock);
            sEvent.wait(lock);

            val = sQueue.front();
            sQueue.pop();
        }
        std::cout << val << std::endl;
    }
}

void Produce()
{
    std::unique_lock<std::mutex> lock(sQueueLock);
    sQueue.push(1);

    sEvent.notify_one();
}

int main()
{
    std::thread producer(Produce);
    std::thread consumer(Consume);

    consumer.join();
    producer.join();

    return 0;
}
// sEvent.notify_one()이 sEvent.wait(lock)보다 먼저 실행되면 무한대기...

std::condition_variable::wait

template< class Predicate >
void wait(std::unique_lock<std::mutex>& lock, Predicate pred);

예시 : 잘못된 조건변수 활용 해결책

#include <iostream>
#include <mutex>
#include <queue>

static std::mutex sQueueLock;
static std::condition_variable sEvent;
static std::queue<int> sQueue;

void Consume()
{
	while(true)
	{
		int val;
		{
			std::unique_lock<std::mutex> lock(sQueueLock);
			sEvent.wait(lock, [] { return !sQueue.empty(); });
		
			val = sQueue.front();
			sQueue.pop();
		}
		std::cout << val << std::endl;
	}
}

void Produce()
{
	std::unique_lock<std::mutex> lock(sQueueLock);
	sQueue.push(1);
	
	sEvent.notify_one();
}

int main()
{
	std::thread producer(Produce);
	std::thread consumer(Consume);
	
	consumer.join();
	producer.join();
	
	return 0;
}