본문 바로가기
연구소👨‍💻/CS연구소

[C++] 스레드 Lock

by 신그자체김상범 2024. 9. 22.

이 글은 이런 것에 대한 정보를 담고 있다

  1. pthread에 대한 내용
  2. pthread에서 lock 구현하는 법
  3. websocketpp 라이브러리를 활용한 프로젝트에서 lock을 사용하는법.

이런 것에 대한 정보는 담겨있지 않다

  1. 뮤텍스와, 세마포어, 크리티컬 섹션등의 상세한 비교
  2. 스레드 사용에 대한 심화 설명

1. pthread란 무엇인가.

프로그래밍에서 필수라고 할 수 있는 멀티스레딩을 위한 라이브러리이다. 유닉스와 리눅스 계열 프로그래밍에서 사용 가능하며 윈도우에서도 호환되는 라이브러리를 설치하면 사용할 수 있다. C++에서는

#include <pthread.h>

코드를 추가함으로써 사용할 수 있다. 다만 pthread 대신 다른 스레드 라이브러리를 사용하기 위해선 thread 라이브러리나 window.h 라이브러리를 include해서 비슷한 기능으로 대체할 수 있다.

2. pthread에서 lock구현하는법

  1. 일단 pthread의 mutex객체를 하나 생성한다
    pthread_mutex_t lock;
    int shared_resource = 0;
  1. 뮤텍스 초기화 코드를 호출한다
         pthread_mutex_init(&lock, NULL);
  1. 특정 자원을 사용해야하는 함수에서 lock 함수를 호출한다
    pthread_mutex_lock(&lock);
    shared_resource++;
    pthread_mutex_unlock(&lock);

그 함수가 끝나기 전데 unlock을 반드시 걸어줘야한다.

3. websocketpp 라이브러리를 활용한 프로젝트에서 lock을 사용하는법

현재 C++에서 웹소켓 서버를 제작하는 일을 하는 중인데. 이를 위해 websocketpp 라이브러리를 사용하고있다. 이 라이브러리도 멀티스레드 환경을 고려해서 thread lock으로 동시성을 제어한다.

class WebsocketServer
{
public:
    static bool init();
    static void run();
    static void stop();

    static bool sendClose(string id);
    static bool sendData(string id, string data);

private:
    static bool getWebsocket(const string &id, websocketpp::connection_hdl &hdl)
    {
        auto socket = websockets.find(id);
        if (socket != websockets.end())
        {
            hdl = socket->second;
            return true;
        }
        return false;
    };

    static websocketpp::server<websocketpp::config::asio> server;
    static pthread_rwlock_t websocketsLock;
    static map<string, websocketpp::connection_hdl> websockets;
    static LogStream ls;
    static ostream os;

    // callbacks
    static bool on_validate(websocketpp::connection_hdl hdl);
    static void on_fail(websocketpp::connection_hdl hdl);
    static void on_close(websocketpp::connection_hdl hdl);
    static void on_message(websocketpp::connection_hdl hdl, websocketpp::server<websocketpp::config::asio>::message_ptr msg);
};

웹소켓 서버의 기능들을 한번 래핑한 클래스다 이 클래스를 통해서 특정 포트를 리슨하고 클라이언트와 상호작용 할 수 있다. 웹소켓 서버는 기본적으로 여러개의 클라이언트와 상호작용하기 때문에 string을 키로 삼고 웹소켓 연결 핸들을 value로 삼는 map 자료구조 websockets을 생성했다.

그런데 여러 스레드에서 동시에 이 websockets을 참조하거나 변경할경우 문제가 발생할 수 있기 때문에 라이브러리가 만들어 놓은 pthread_rwlock_t 객체를 하나 생성한다.

그리고 클라이언트가 연결될 때 훅으로 걸리는 on_validate 코드에서

WebsocketServer::on_validate(connection_hdl hdl){

        ...

        if (pthread_rwlock_wrlock(&websocketsLock) != 0)
    {
              return false;
        // Failed to write-lock websocketsLock.
    }

    websockets.insert(std::pair<string, connection_hdl>(id, hdl));
    if (pthread_rwlock_unlock(&websocketsLock) != 0)
    {
            return false;
        // Failed to unlock websocketsLock.
    }
}

websockets객체에 insert를 하기전에 websocketsLock을 잠구는 함수가 실패했을 경우 즉 현재 사용중일경우 쓰기를 취소하고 끝난뒤에는 unlock에 실패했을경우 각각 에러를 리턴한다.