[c++] CAS ๊ตฌํ˜„ ๋ฐ ABA ๋ฌธ์ œ ํ•ด๊ฒฐ :: ABA ํ•ด๊ฒฐ_2. hazard pointer

 

๋ชฉ์ฐจ

  1. ๋ฌธ์ œ ์ •์˜
  2. lock free ๊ตฌํ˜„
  3. ABA ํ•ด๊ฒฐ
    1. intํ˜• ๊ตฌํ˜„(+ Hazard pointer)
    2. Counter
    3. ๊ทธ ์™ธ์˜ ๋ฐฉ๋ฒ•๋“ค
  4. mutex lock(spin lock)๊ณผ์˜ ๋น„๊ต

 

Hazard Pointer

https://m.blog.naver.com/PostView.nhn?blogId=jjoommnn&logNo=130127286459&proxyReferer=https:%2F%2Fwww.google.com%2F ๋ฅผ ์ฐธ๊ณ ํ•˜์—ฌ ์ž‘์„ฑํ•˜์˜€๋‹ค.

 

๋ฌธ์ œ์ 

๋จผ์ € delete๋ฅผ ์ด์šฉํ•˜์—ฌ ๊ฐ์ฒด๋ฅผ ์‚ญ์ œํ•˜๋ฉด,

  1. ํ•ด๋‹น ๊ฐ์ฒด๋ฅผ ์ฐธ์กฐํ•˜๊ณ  ์žˆ๋˜ ์Šค๋ ˆ๋“œ์— ๋ฌธ์ œ๊ฐ€ ์ƒ๊ธธ ์ˆ˜๋„ ์žˆ๊ณ 
  2. ๊ฐ์ฒด๊ฐ€ ์‚ญ์ œ๋œ ์ฃผ์†Œ์— ๋‹ค์‹œ ๊ฐ™์€ ๊ฐ์ฒด๊ฐ€ ํ• ๋‹น๋˜์–ด ABA ๋ฌธ์ œ๊ฐ€ ์ผ์–ด๋‚  ๊ฐ€๋Šฅ์„ฑ์ด ์žˆ๋‹ค.
  3. ์ƒˆ๋กœ ๊ฐ์ฒด๋ฅผ ํ• ๋‹นํ•˜๋Š” ๊ฒƒ์€ ์‚ฌ์šฉ์ž๊ฐ€ ๊ด€๋ฆฌํ•˜๋Š” list๊ฐ€ ์•„๋‹ˆ๋‹ค.

1๋ฒˆ ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•˜๊ธฐ ์œ„ํ•ด์„œ Hazard pointer๋ฅผ ๊ตฌํ˜„ํ•ด๋ณด์•˜์œผ๋‚˜, 2, 3๋ฒˆ ๋ฌธ์ œ ๋•Œ๋ฌธ์— ๊ฒฐ๊ตญ ์ƒˆ๋กœ์šด ๊ฐ์ฒด๋ฅผ ๋งŒ๋“œ๋Š” ๋ฐฉ๋ฒ•์€ ABA ๋ฌธ์ œ์˜ ์˜จ์ „ํ•œ ํ•ด๊ฒฐ์ฑ…์ด ๋˜์ง€ ๋ชปํ•œ๋‹ค. ๊ทธ๋ž˜๋„ ํ—ค์ €๋“œ ํฌ์ธํ„ฐ์˜ ์›๋ฆฌ๋ฅผ ๊ณต๋ถ€ํ•˜๊ณ  ์ฝ”๋“œ๋ฅผ ๊ตฌํ˜„ํ•ด๋ณด์•˜๋‹ค.

 

์›๋ฆฌ

์šฐ์„  ๊ฐ ์“ฐ๋ ˆ๋“œ๋งˆ๋‹ค ์‚ญ์ œ๊ฐ€ ํ•„์š”ํ•œ ๋…ธ๋“œ๋“ค์„ ๋‹ด์•„๋‘๋Š” list๋ฅผ ๊ฐ€์ง€๊ณ  ์žˆ์–ด์•ผํ•œ๋‹ค. ์ด๋ฅผ RetireList๋ผ๊ณ  ํ•˜์ž. ์“ฐ๋ ˆ๋“œ๋“ค์€ pop์„ ํ•  ๋•Œ๋งˆ๋‹ค ํ•„์š”์—†์–ด์ง„ ๋…ธ๋“œ๋“ค์„ ์‚ญ์ œํ•˜๊ธฐ ์œ„ํ•ด RetireList์— ๋‹ด์•„๋‘”๋‹ค. ํ•˜์ง€๋งŒ ์ด ๋…ธ๋“œ๋“ค์ด ์ฐธ์กฐ๋˜์ง€ ์•Š์„ ๋•Œ ์‚ญ์ œํ•ด์•ผํ•˜๋ฏ€๋กœ ๋ชจ๋“  ์“ฐ๋ ˆ๋“œ๊ฐ€ ๊ณต์œ ํ•˜๋Š” Hazard pointer๋ฅผ ๋งŒ๋“ ๋‹ค.

 Hazard pointer๋Š” ์—ฐ๊ฒฐ๋ฆฌ์ŠคํŠธ ํ˜•ํƒœ๋กœ ๊ตฌ์„ฑ๋˜์–ด ์žˆ์œผ๋ฉฐ ์–ด๋–ค ๋…ธ๋“œ๊ฐ€ ์ฐธ์กฐ๋˜๊ณ  ์žˆ๋Š”์ง€๋ฅผ ๋‚˜ํƒ€๋‚ด๋Š” ์—ญํ• ์„ ํ•œ๋‹ค. Hazard pointer์˜ ๊ฐ ๊ณต๊ฐ„์€ ํ˜„์žฌ ํ•ด๋‹น ๊ณต๊ฐ„์ด ์‚ฌ์šฉ๋˜๊ณ  ์žˆ๋Š”์ง€๋ฅผ ๋‚˜ํƒ€๋‚ด๋Š” active ๋ณ€์ˆ˜์™€ ์‚ฌ์šฉํ•˜๊ณ  ์žˆ๋Š” ๋…ธ๋“œ๋ฅผ ๊ฐ€๋ฆฌํ‚ค๋Š” node ๋ณ€์ˆ˜, ๊ทธ๋ฆฌ๊ณ  ๋‹ค์Œ ๊ณต๊ฐ„์„ ๊ฐ€๋ฆฌํ‚ค๋Š” next ๋ณ€์ˆ˜๋กœ ๊ตฌ์„ฑ๋˜์–ด์žˆ๋‹ค.

 ๋จผ์ € ๊ฐ ์Šค๋ ˆ๋“œ๋Š” pop ์‹œ Hazard pointer์— ๊ณต๊ฐ„์„ ํ•˜๋‚˜ ๋ถ€์—ฌ๋ฐ›๋Š”๋‹ค. ๊ณต๊ฐ„์„ ๋ถ€์—ฌ ๋ฐ›์„ ๋•Œ์—๋Š” ํ˜„์žฌ active๊ฐ€ ์•„๋‹Œ ๊ณต๊ฐ„์ด ์žˆ๋Š” ์ง€ ํ™•์ธํ•˜๊ณ , ์žˆ๋‹ค๋ฉด ํ•ด๋‹น ๊ณต๊ฐ„์„ ์žฌ์‚ฌ์šฉํ•œ๋‹ค. (์ด๋ ‡๊ฒŒ ํ•˜์ง€ ์•Š์œผ๋ฉด ๊ณต๊ฐ„์ด ๊ณ„์† ๋Š˜์–ด๋‚˜ ์ˆ˜ํ–‰์‹œ๊ฐ„์ด ๋งค์šฐ ์˜ค๋ž˜ ๊ฑธ๋ฆฐ๋‹ค.) ๊ณต๊ฐ„์„ ํ• ๋‹น ๋ฐ›์€ ํ›„์—๋Š” ํ•ด๋‹น ๊ณต๊ฐ„์˜ active ๋ณ€์ˆ˜๋ฅผ 1๋กœ ๋งŒ๋“ค์–ด ๊ณต๊ฐ„์„ ์‚ฌ์šฉํ•˜๊ฒ ๋‹ค๊ณ  ํ‘œ์‹œํ•˜๊ณ  ์‚ฌ์šฉํ•  ๋…ธ๋“œ(list์—์„œ popํ•  ๋…ธ๋“œ)๋ฅผ node ๋ณ€์ˆ˜์— ๋„ฃ๋Š”๋‹ค. ์ด๋Ÿฌํ•œ ์ฝ”๋“œ๋Š” ๋‹ค๋ฅธ ์Šค๋ ˆ๋“œ์—๊ฒŒ ์ด node๋Š” ํ˜„์žฌ ์‚ฌ์šฉ ์ค‘์ด๋‹ˆ ์‚ญ์ œํ•˜์ง€ ๋ง๋ผ๊ณ  ํ‘œ์‹œํ•˜๋Š” ์—ญํ• ์„ ํ•œ๋‹ค.

 tmp_next๋ฅผ ํ• ๋‹นํ•˜๊ณ  popํ•  data๋ฅผ ์ €์žฅํ•œ ๋’ค CAS๋ฅผ ์‹œํ–‰ํ•œ๋‹ค. CAS๊ฐ€ ์„ฑ๊ณตํ•˜๊ณ  ๋‚˜์„œ๋Š” ํ• ๋‹น ๋ฐ›์€ Hazard pointer์˜ ๊ณต๊ฐ„์„ ๋ฐ˜๋‚ฉํ•˜๊ณ  tmp๊ฐ€ ๊ฐ€๋ฆฌํ‚ค๊ณ  ์žˆ๋Š” ๋…ธ๋“œ๋ฅผ ํ˜„์žฌ ์“ฐ๋ ˆ๋“œ์˜ RetireList์— ๋‹ด๋Š”๋‹ค.

 RetireList์— ๋…ธ๋“œ๊ฐ€ ๋‹ด๊ธธ ๋•Œ๋งˆ๋‹ค Hazard pointer์— ์žˆ๋Š” ๋ชจ๋“  ์‚ฌ์šฉ ์ค‘์ธ ๋…ธ๋“œ๋“ค์„ ํ™•์ธํ•˜๊ณ  ์ง€์šด๋‹ค๋ฉด ๋„ˆ๋ฌด ์˜ค๋ž˜๊ฑธ๋ฆฌ๋ฏ€๋กœ RetireList์— ๋‹ด๊ธด ๋…ธ๋“œ๊ฐ€ ํŠน์ • ์ˆ˜ ์ด์ƒ์ด ๋  ๋•Œ ํ•œ๊บผ๋ฒˆ์— ๊ฒ€์‚ฌํ•˜๊ณ  ์‚ญ์ œํ•˜๊ธฐ๋กœ ํ•˜์ž. (์‹ค์ œ GC๋„ ์–ธ์ œ ์‹คํ–‰๋  ์ง€ ์‚ฌ์šฉ์ž๊ฐ€ ์•Œ ์ˆ˜ ์—†๋‹ค.) ์—ฌ๊ธฐ์„œ RetireList๋Š” ์“ฐ๋ ˆ๋“œ๋งˆ๋‹ค ํ•˜๋‚˜์”ฉ ๊ฐ€์ง€๊ณ  ์žˆ๊ธฐ ๋•Œ๋ฌธ์— ๋™๊ธฐํ™”ํ•  ํ•„์š”๊ฐ€ ์—†๋‹ค. ๋”ฐ๋ผ์„œ ์ฝ”๋“œ์—์„œ๋Š” vector ์ž๋ฃŒ๊ตฌ์กฐ๋กœ ๊ตฌํ˜„ํ•˜์˜€๋‹ค.

 

์˜๋ฌธ์ 

 ๊ทธ๋ ‡๋‹ค๋ฉด Hazard Pointer๋Š” ์“ฐ๋ ˆ๋“œ๊ฐ€ ๊ณต์œ ํ•˜๋Š” ์ž์›์ธ๋ฐ ์ƒํ˜ธ๋ฐฐ์ œ๊ฐ€ ํ•„์š”์—†์„๊นŒ? atomic ๋ณ€์ˆ˜๋ฅผ ์ด์šฉํ•ด์„œ CAS์—ฐ์‚ฐ์œผ๋กœ active setting์„ ํ•˜๊ธฐ ๋•Œ๋ฌธ์— lock-free๋กœ ๊ตฌํ˜„๊ฐ€๋Šฅํ•˜๋‹ค.

 ๊ทธ๋ ‡๋‹ค๋ฉด ABA ๋ฌธ์ œ๋Š” ์•ˆ ์ƒ๊ธธ๊นŒ? Hazard Pointer๋Š” pushํ•˜๋Š” ์ฝ”๋“œ๋งŒ ์žˆ์ง€ popํ•˜๋Š” ์ฝ”๋“œ๊ฐ€ ์—†๋‹ค. ์“ฐ๋ ˆ๋“œ๋“ค์€ ๋นˆ๊ณต๊ฐ„์„ ์ฐพ์•„์„œ ํ• ๋‹นํ•˜๊ณ  ์—†์œผ๋ฉด ๊ณต๊ฐ„์„ ์ƒˆ๋กœ ํ•˜๋‚˜ ๋งŒ๋“ ๋‹ค. ํ• ๋‹น๋ฐ›์€ ๋ฉ”๋ชจ๋ฆฌ๋ฅผ ๋ฐ˜๋‚ฉํ•˜๋Š” ๊ฒƒ๋„ ์‚ฌ์‹ค์€ Hazard Pointer์˜ ๊ณต๊ฐ„ ์ž์ฒด๋ฅผ ์—†์• ๋Š” ๊ฒƒ์ด ์•„๋‹ˆ๋ผ ํ•ด๋‹น ๊ณต๊ฐ„์˜ active ๋ณ€์ˆ˜๋ฅผ 0์œผ๋กœ ๋งŒ๋“ค๊ณ  node setting์„ ์ดˆ๊ธฐํ™”ํ•˜๋Š” ๊ฒƒ ๋ฟ์ด๋‹ค. ๋”ฐ๋ผ์„œ ABA ๋ฌธ์ œ๋„ ์•ˆ ์ƒ๊ธด๋‹ค.

 

์ฝ”๋“œ

์ ˆ์ฐจ์ง€ํ–ฅ ๋ฐฉ์‹์œผ๋กœ ๊ตฌํ˜„๋˜์–ด์žˆ๋‹ค

#define _CRT_SECURE_NO_WARNINGS
#define NULL_INT -1

#include <iostream>
#include <thread>
#include <vector>
#include <algorithm>
#include <atomic>
#include <mutex>
// #include <ctime>
#include <random>
#include <intrin.h>


using namespace std;

struct Node {
    int data;
    Node* next_node;
};

int node_n;
atomic<Node*> free_list;
atomic<Node*> head_list;

//์ถœ๋ ฅ์„ ์œ„ํ•œ mutex
mutex mut;

//random ๊ตฌํ˜„
random_device rd;
mt19937 gen(rd());
uniform_int_distribution<> dis(0, 10);


// Hazard pointer ๊ตฌํ˜„
struct HzPRec {
    atomic<int> active;
    Node* node;
    HzPRec* next;
};
atomic<HzPRec*> pHpHead;
vector<Node*> RetireList[3];
atomic<int> DWORD = 0;

// Hazard pointer ํ•จ์ˆ˜
HzPRec* AllocHp() {
    HzPRec* p;

    for (p = pHpHead.load(); p != nullptr; p = p->next) { // ํ˜„์žฌ ๋นˆ๊ณต๊ฐ„์ด ์žˆ๋Š”์ง€ ํ™•์ธ
        int active;
        active = p->active.load();
        if (active == 0) {
            if (atomic_compare_exchange_weak(&(p->active), &active, 1))
                return p;
        }
    }

    // ์—†์œผ๋ฉด ์ƒˆ๋กœ HP ๋…ธ๋“œ ํ•˜๋‚˜ ๋งŒ๋“ค๊ธฐ
    p = new HzPRec();
    p->active = 1;
    p->node = nullptr;

    // ์‚ฝ์ž… => aba ์•ˆ์ผ์–ด๋‚˜๋Š” ์ด์œ ๋Š” ์‚ญ์ œ๊ฐ€ ์—†๊ธฐ ๋•Œ๋ฌธ
    HzPRec* oldHead;
    do {
        oldHead = pHpHead.load();
        p->next = oldHead;
    } while (!atomic_compare_exchange_weak(&pHpHead, &oldHead, p));

    return p;
}

void ReleaseHp(HzPRec* p) { // ๋ฏธ์‚ฌ์šฉ์ค‘์œผ๋กœ ๋งŒ๋“ค๊ธฐ
    p->active = 0;
    p->node = nullptr;
}

void Scan(int t_n) {
    vector<Node*> hpList;
    for (HzPRec* p = pHpHead.load(); p != nullptr; p = p->next) {
        if (p->node != NULL)
            hpList.push_back(p->node);
    }

    sort(hpList.begin(), hpList.end());

    Node* p = RetireList[t_n].back();
    RetireList[t_n].pop_back();
    // thread๋งˆ๋‹ค ๊ฐ๊ฐ ๊ฐ€์ง€๊ณ  ์žˆ๋Š” ์ž๋ฃŒ๊ตฌ์กฐ์ด๊ธฐ ๋•Œ๋ฌธ์— RetireList๋Š” ์ƒํ˜ธ๋ฐฐ์ œ ํ•„์š”์—†์Œ

    Node* tmp;
    int index = 0;
    while (p != nullptr) { // RetireList๋ฅผ ํ•˜๋‚˜์”ฉ ๊บผ๋‚ด๋ฉด์„œ ํ™•์ธ
        int flag = 0;
        for (int i = 0; i < hpList.size(); i++) {
            if (hpList[i] == NULL)
                continue;
            if (hpList[i]->data == p->data) { // ํ˜„์žฌ ์‚ฌ์šฉ์ค‘์ธ ๋…ธ๋“œ์ธ๊ฐ€?
                flag = 1;
                break;
            }
        }
        if (!flag) {
            // ๋…ธ๋“œ๊ฐ€ ์‚ฌ์šฉ ์ค‘์ด ์•„๋‹ˆ๋ผ๋ฉด
            delete p; // ๋ฉ”๋ชจ๋ฆฌ ๋ฐ˜๋‚ฉ
            if (index < RetireList[t_n].size() - 1) {
                p = RetireList[t_n].back(); // ๋‹ค์Œ ๋…ธ๋“œ ๊ฒ€์‚ฌ
                RetireList[t_n].pop_back();
            }
            else
                p = nullptr;
        }
        else {
            // ๋…ธ๋“œ๊ฐ€ ์‚ฌ์šฉ ์ค‘์ด๋ผ๋ฉด
            if (index < RetireList[t_n].size() - 1) {
                p = RetireList[t_n].back();
                RetireList[t_n].pop_back();
            }
            else
                p = nullptr;
        }
    }
}

void RetireNode(Node* node, int t_n) {
    RetireList[t_n].push_back(node);

    if (RetireList[t_n].size() > 200) { // ๋…ธ๋“œ๊ฐ€ 200๊ฐœ ์ด์ƒ์ผ ๋•Œ ๋ฉ”๋ชจ๋ฆฌ ๋ฐ˜๋‚ฉ
        Scan(t_n);
    }
}

// push, pop ํ•จ์ˆ˜, print ํ•จ์ˆ˜


void printNodes(int type) { // list ์ถœ๋ ฅ ํ•จ์ˆ˜
    Node* tmp;
    if (type == 0) {
        cout << "head_list ";
        tmp = head_list.load();
    }
    else {
        cout << "free_list ";
        tmp = free_list.load();
    }
    int num = 0;
    while (tmp != nullptr) {
        //cout << tmp->data << " ";
        tmp = tmp->next_node;
        num++;

        if (num > 100000) { // error ์ฒ˜๋ฆฌ
            cout << "node is more than 100000" << endl;
            return;
        }
    }
    cout << endl;
    cout << num << endl;
}

void push(int data) {
    Node* tmp_node = new Node();
    tmp_node->data = data;
    Node* tmp;
    do {
        tmp = head_list.load();
        tmp_node->next_node = tmp;
    } while (!atomic_compare_exchange_weak(&head_list, &tmp, tmp_node));
}

void push_f(int data) {
    Node* tmp_node = new Node();
    tmp_node->data = data;
    Node* tmp;
    do {
        tmp = free_list.load();
        tmp_node->next_node = tmp;
    } while (!atomic_compare_exchange_weak(&free_list, &tmp, tmp_node));
}

int pop(int t_n) {
    Node* tmp; Node* tmp_next;
    HzPRec* hp = AllocHp(); // ๋ฉ”๋ชจ๋ฆฌ ํ• ๋‹น
    while (true) {
        if ((tmp = head_list.load()) == nullptr) {
            ReleaseHp(hp); // ๋ฉ”๋ชจ๋ฆฌ ํ•ด์ œ
            return NULL_INT;
        }
        // head_list์— data๊ฐ€ ๋“ค์–ด์˜ฌ ๋•Œ๊นŒ์ง€ ๊ธฐ๋‹ค๋ฆฐ๋‹ค.
        hp->node = tmp;

        if (tmp != head_list.load()) // tmp_next ์ „์— ๋‹ค์‹œ ํ•œ ๋ฒˆ ํ™•์ธ
            continue;

        tmp_next = tmp->next_node;
        int data = tmp->data;

        if (atomic_compare_exchange_weak(&head_list, &tmp, tmp_next)) {
            ReleaseHp(hp); // ๋ฉ”๋ชจ๋ฆฌ ํ•ด์ œ
            RetireNode(tmp, t_n); // ์‚ญ์ œํ•  ๋…ธ๋“œ์— ๋“ฑ๋ก
            return data;
        }
    }
}

int pop_f(int t_n) {
    Node* tmp; Node* tmp_next;
    HzPRec* hp = AllocHp();
    while (true) {
        if ((tmp = free_list.load()) == nullptr) {
            ReleaseHp(hp);
            return NULL_INT;
        }
        // head_list์— data๊ฐ€ ๋“ค์–ด์˜ฌ ๋•Œ๊นŒ์ง€ ๊ธฐ๋‹ค๋ฆฐ๋‹ค.
        hp->node = tmp;

        if (tmp != free_list.load())
            continue;

        tmp_next = tmp->next_node;
        int data = tmp->data;

        if (atomic_compare_exchange_weak(&free_list, &tmp, tmp_next)) {
            ReleaseHp(hp);
            RetireNode(tmp, t_n);
            return data;
        }
    }
}

void subThread() {
    // random n, m
    int n = dis(gen);
    int m = dis(gen);
    int thread_num = DWORD;
    DWORD.fetch_add(1);

    mut.lock();
    cout << n << " " << m << endl;
    mut.unlock();

    int num = 10000;
    while (num--) {
        for (int i = 0; i < n; i++)
        {
            // free_list์— ์žˆ์œผ๋ฉด head_list์— ์˜ฎ๊ธฐ๋Š” ์ž‘์—…
            int tmp = pop_f(thread_num);
            if (tmp == NULL_INT)
                continue;
            push(tmp);
        }
        for (int i = 0; i<m; i++) {
            // head_list์— ์žˆ์œผ๋ฉด free_list์— ์˜ฎ๊ธฐ๋Š” ์ž‘์—…
            int tmp = pop(thread_num);
            if (tmp == NULL_INT)
                continue;
            push_f(tmp);
        }
    }
}

int main() {
    // ๋…ธ๋“œ ์ƒ์„ฑ ๋ฐ free_list ์‚ฝ์ž…
    node_n = 100000; // ๋…ธ๋“œ ๊ฐฏ์ˆ˜
    for (int i = node_n - 1; i >= 0; i--) {
        push_f(i);
    }


    // ์Šค๋ ˆ๋“œ ์ƒ์„ฑ ๋ฐ ๋™์ž‘ ์‹œ์ž‘ 
    vector<thread> threads;
    int n = 3; // ์Šค๋ ˆ๋“œ ๊ฐฏ์ˆ˜
    for (int i = 0; i < n; i++) {
        RetireList[i] = (vector<Node*>());
        threads.emplace_back(subThread);
    }
    for (auto &t : threads)
        t.join();
    threads.clear();

    printNodes(0);
    printNodes(1);
    cin >> n;

}

 

 

๊ฒฐ๊ณผ

  • ์‹คํ–‰ ์กฐ๊ฑด
๋…ธ๋“œ ๊ฐฏ์ˆ˜ thread while๋ฌธ ์ˆ˜ํ–‰ํšŸ์ˆ˜
100,000๊ฐœ 3๊ฐœ 10,000ํšŒ

 

  • ๊ฒฐ๊ณผ
๊ตฌ๋ถ„ free_list ๋…ธ๋“œ ์ˆ˜ head_list ๋…ธ๋“œ ์ˆ˜
1 100,000 0
2 50,004 49,996
3 40,000 60,000

๋Œ๋ ค๋ณธ ๊ฒฐ๊ณผ ์ž˜ ๋™์ž‘ํ•˜์ง€๋งŒ ์œ„์—์„œ ์–ธ๊ธ‰ํ–ˆ๋˜ ๋ฌธ์ œ๋“ค ๋•Œ๋ฌธ์— Hazard pointer๋Š” ์˜จ์ „ํ•œ ํ•ด๊ฒฐ์ฑ…์ด ๋˜์ง€ ๋ชปํ•œ๋‹ค. ๋‹ค๋ฅธ ๋ฐฉ๋ฒ•์„ ์ฐพ์•„๋ณด์ž.

 

=> Counter ๋ฐฉ๋ฒ•์€ ๋‹ค์Œ ํฌ์ŠคํŒ…์—

๋ฐ˜์‘ํ˜•