(C++ : Concurrency) 6. mutex

Posted by : at

Category : Cpp


스레드 동기화

#include <iostream>
#include <thread>
#include <chrono>
#include <string_view>
using namespace std::literals;

void delay() { std::this_thread::sleep_for(20ms); }

void foo(std::string_view name)
{
    int x = 0;          // 지역변수는 스레드에 안전!
    static int x = 0;   // 전역변수라면? 스레드에 안전하지 못하다

    for(int i = 0; i < 10; i++)
    {
        x = 100; delay();
        x = x + 1; delay();
        std::cout << name << " : " << x << std::endl; delay();
    }
}

int main()
{
    std::thread t1(foo, "A");
    std::thread t2(foo, "\tB");
    t1.join();
    t2.join();
}
std::mutex m;

// ...

void foo(std::string_view name)
{
    static int x = 0;

    for(int i = 0; i < 10; i++)
    {
        m.lock();
        x = 100; delay();
        x = x + 1; delay();
        std::cout << name << " : " << x << std::endl; delay();
        m.unlock();
    }
}

// ...

mutex 상세

  • mutex종류
    • std::mutex
    • std::timed_mutex
    • std::recursive_mutex
    • std::recursive_timed_mutex
    • std::shared_mutex (C++17)
    • std::shared_timed_mutex (C++17)
#include <iostream>
#include <thread>
#include <chrono>
#include <mutex>
using namespace std::literals;

std::mutex m;
int share_data = 0;

void foo()
{
    if(m.try_lock())
    {
        share_data = 100;
        stds::cout << "using shared_data" << std::endl;
        m.unlock();
    }
    else
    {
        stds::cout << "mutex획득실패" << std::endl;
    }
}

int main()
{
    std::thread t1(foo);
    std::thread t2(foo);
    t1.join();
    t2.join();

    // OS에서 사용중인 mutex 핸들을 리턴
    std::mutex::native_handle_type h = m.native_handle();

    std::mutex m2 = m;  // Error - 복사생성이 안됨, Move도 안됨
}

mutex Vs timed_mutex

  • timed_mutex : try_lock_for, try_lock_until을 지원한다
#include <iostream>
#include <thread>
#include <chrono>
#include <mutex>
using namespace std::literals;

std::timed_mutex m;
int share_data = 0;

void foo()
{
    if(m.try_lock_for(2s))
    {
        share_data = 100;
        stds::cout << "using shared_data" << std::endl;
        m.unlock();
    }
    else
    {
        stds::cout << "mutex획득실패" << std::endl;
    }
}

int main()
{
    std::thread t1(foo);
    std::thread t2(foo);
    t1.join();
    t2.join();
}

recursive_mutex

// ...

std::mutex m;
int share_data = 0;

void foo()
{
    m.lock();
    m.lock();   // Error - 다시 lock할순 없다 -> recursive_mutex이용
    // ...
#include <iostream>
#include <thread>
#include <chrono>
#include <mutex>
using namespace std::literals;

std::recursive_mutex m;
int share_data = 0;

void foo()
{
    m.lock();
    m.lock();   // 내부적으로 소유횟수를 관리함을 기억
    share_data = 100;
    stds::cout << "using shared_data" << std::endl;
    m.unlock(); // 다른 thread가 접근하지못함, 한 번더 unlock해야한다
}

int main()
{
    std::thread t1(foo);
    std::thread t2(foo);
    t1.join();
    t2.join();
}

Example

class Machine
{
    int shared_data = 0;
    std::recursive_mutex m;
public:
    void f1()
    {
        m.lock();
        shared_data = 100;
        m.unlock();
    }
    void f2()
    {
        m.lock();
        shared_data = 200;
        f1();
        m.unlock();
    }
};

shared_mutex

#include <iostream>
#include <thread>
#include <chrono>
#include <mutex>
#include <string_view>
using namespace std::literals;

std::mutex m;
int share_data = 0;

void Writer()
{
    while(1)
    {
        m.lock();
        share_data = share_data + 1;
        std::cout << "Writer : " << share_data << std::endl;
        std::this_thread::sleep_for(1s);
        m.unlock();
        std::this_thread::sleep_for(10ms);
    }
}

void Reader(std::string_view name)
{
    while(1)
    {
        m.lock();
        std::cout << "Reader(" << name << ") : " << share_data << std::endl;
        std::this_thread::sleep_for(500ms);
        m.unlock();
        std::this_thread::sleep_for(10ms);
    }
}

int main()
{
    std::thread t1(Writer);
    std::thread t2(Reader, "A");
    std::thread t3(Reader, "B");
    std::thread t4(Reader, "C");
    t1.join();
    t2.join();
    t3.join();
    t4.join();
    // 여기서 생각해 봐야할 문제는 쓰는동안에 읽는건 문제가 될 수 있지만
    // 다른 스레드에서 읽는동안에 같이 읽는건 문제될게 없지 않나??

    // 개선해보자
}
#include <iostream>
#include <thread>
#include <chrono>
#include <mutex>
#include <string_view>
using namespace std::literals;

std::shared_mutex m;
int share_data = 0;

void Writer()
{
    while(1)
    {
        m.lock();
        share_data = share_data + 1;
        std::cout << "Writer : " << share_data << std::endl;
        std::this_thread::sleep_for(1s);
        m.unlock();
        std::this_thread::sleep_for(10ms);
    }
}

void Reader(std::string_view name)
{
    while(1)
    {
        m.lock_shared();
        std::cout << "Reader(" << name << ") : " << share_data << std::endl;
        std::this_thread::sleep_for(500ms);
        m.unlock_shared();
        std::this_thread::sleep_for(10ms);
    }
}

int main()
{
    std::thread t1(Writer);
    std::thread t2(Reader, "A");
    std::thread t3(Reader, "B");
    std::thread t4(Reader, "C");
    t1.join();
    t2.join();
    t3.join();
    t4.join();
}

About Taehyung Kim

안녕하세요? 8년차 현업 C++ 개발자 김태형이라고 합니다. 😁 C/C++을 사랑하며 다양한 사람과의 협업을 즐깁니다. ☕ 꾸준한 자기개발을 미덕이라 생각하며 노력중이며, 제가 얻은 지식을 홈페이지에 정리 중입니다. 좀 더 상세한 제 이력서 혹은 Private 프로젝트 접근 권한을 원하신다면 메일주세요. 😎

Star
Useful Links