[c++] CAS ๊ตฌํ˜„ ๋ฐ ABA ๋ฌธ์ œ ํ•ด๊ฒฐ :: ๋ฌธ์ œ ์ •์˜, ๊ธฐ๋ณธ CAS ๊ตฌํ˜„

๋ชฉ์ฐจ

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

 

๋ฌธ์ œ ์ •์˜

๋ฌธ์ œ

Linked List ์ž๋ฃŒ๊ตฌ์กฐ์—์„œ push()์™€ pop() ์—ฐ์‚ฐ์„ ๊ตฌํ˜„ํ•œ๋‹ค. Linked List๋Š” FreeList์™€ HeadList ๋‘๊ฐ€์ง€๊ฐ€ ์žˆ์œผ๋ฉฐ ์—ฌ๋Ÿฌ ์Šค๋ ˆ๋“œ๊ฐ€ ๋‘ ๊ฐœ์˜ List๋ฅผ ๋ฒˆ๊ฐˆ์•„๊ฐ€๋ฉฐ push, pop ์—ฐ์‚ฐ์„ ์ˆ˜ํ–‰ํ•œ๋‹ค. ์Šค๋ ˆ๋“œ๋“ค์ด ๊ฐ๊ฐ ์—ฐ์‚ฐ์„ ์ˆ˜ํ–‰ํ•˜๊ธฐ ์œ„ํ•ด์„œ๋Š” List๋“ค์— ๋Œ€ํ•œ ์ƒํ˜ธ๋ฐฐ์ œ๊ฐ€ ํ•„์š”ํ•˜๋‹ค.

ํ•ด์„

์ƒํ˜ธ ๋ฐฐ์ œ์—๋Š” ์—ฌ๋Ÿฌ๊ฐ€์ง€ ๋ฐฉ๋ฒ•์ด ์žˆ์ง€๋งŒ, ํฌ๊ฒŒ ๋‘๊ฐ€์ง€๋กœ ๋‚˜๋ˆŒ ์ˆ˜ ์žˆ๋‹ค. lock ๋ณ€์ˆ˜์˜ ์กฐ๊ฑด์„ ์ฒดํฌํ•˜๋ฉฐ ๋ฐ˜๋ณต๋ฌธ์„ ๋Œ๊ณ  ์žˆ๋Š” spin lock๊ณผ, lock ๋ณ€์ˆ˜ ์—†์ด ๊ณ„์† ์‹œ๋„ํ•˜๋ฉด์„œ ์ ์ ˆํ•œ ๋•Œ์— ๋™์ž‘์„ ์ˆ˜ํ–‰ํ•˜๋Š” lock free ๊ธฐ๋ฒ•์ด ๊ทธ๊ฒƒ์ด๋‹ค. spin lock๊ณผ lock free ๊ธฐ๋ฒ•์˜ ์ฐจ์ด์ ์€ ์ด๋ฆ„์—์„œ ๋“œ๋Ÿฌ๋‚˜๋“ฏ์ด lock ๋ณ€์ˆ˜์˜ ์œ ๋ฌด์ด๋‹ค. lock์„ ์ด์šฉํ•œ ์ž„๊ณ„๊ตฌ์—ญ ๊ตฌํ˜„์ด ๊ฐ ์Šค๋ ˆ๋“œ๊ฐ€ ํ•˜๋‚˜์”ฉ ์ ‘๊ทผํ•  ์ˆ˜ ์žˆ๊ฒŒ ๋งŒ๋“ ๊ฑฐ๋ผ๋ฉด, lock free์˜ CAS ๊ธฐ๋ฒ•์€ ๋ชจ๋“  ์Šค๋ ˆ๋“œ๊ฐ€ ์ ‘๊ทผํ•˜๋ ค๊ณ  ๊ณ„์† ์• ์“ฐ๋‹ค๊ฐ€ ๋ฌด์ž‘์œ„์ ์œผ๋กœ ํƒ€์ด๋ฐ์— ๋งž๋Š” ์Šค๋ ˆ๋“œ๊ฐ€ ์ ‘๊ทผํ•˜๋Š” ๊ฒƒ์ด๋‹ค. lock free ๊ธฐ๋ฒ•์„ ํ™œ์šฉํ•˜๋ฉด ์ž„๊ณ„๊ตฌ์—ญ์„ ๊ตฌํ˜„ํ•˜์ง€ ์•Š์•„๋„ ๋˜๊ณ , lock ๋ณ€์ˆ˜ ๋ณ‘๋ชฉ์ด ์ค„์–ด๋“œ๋ฉฐ ๋ณ‘๋ ฌํ™” ์ •๋„๋ฅผ ํ–ฅ์ƒ์‹œํ‚ฌ ์ˆ˜ ์žˆ๋‹ค.

lock free ๊ธฐ๋ฒ•์—๋Š” CAS๊ฐ€ ๋Œ€ํ‘œ์ ์ด๊ณ , ์ด๋ฅผ ๊ตฌํ˜„ํ•˜๋Š” ๋ฐฉ๋ฒ•์€ C++, C# ๋“ฑ ์—ฌ๋Ÿฌ ๊ฐ€์ง€ ์–ธ์–ด์—์„œ ์ง€์›ํ•˜๊ณ  ์žˆ๋‹ค. ํ•˜์ง€๋งŒ CAS ๊ตฌํ˜„ ์‹œ ABA ๋ฌธ์ œ๋ผ๋Š” ๊ฒƒ์ด ์ผ์–ด๋‚  ์ˆ˜ ์žˆ๋Š”๋ฐ, ์ด๋Š” ํ”„๋กœ๊ทธ๋ž˜๋จธ๊ฐ€ ๊ฐ์ž ํ•ด๊ฒฐํ•ด์•ผํ•˜๋Š” ๋ถ€๋ถ„์ด๋‹ค.

CAS ์ด์šฉ ์‹œ ๋™์ž‘๊ณผ์ •์€ ์–ด๋–ป๊ฒŒ ๋˜๋ฉฐ, ABA ๋ฌธ์ œ๊ฐ€ ์ผ์–ด๋‚ฌ์„ ๋•Œ ํ•ด๊ฒฐํ•  ์ˆ˜ ์žˆ๋Š” ๋ฐฉ๋ฒ•์€ ๋ฌด์—‡์ด ์žˆ๋Š”์ง€ ์‚ดํŽด๋ณด์ž. ๋˜ํ•œ ๋งˆ์ง€๋ง‰ ์ด๋Ÿฌํ•œ lock free ๊ธฐ๋ฒ•์ด spin lock ๊ณผ ๋น„๊ตํ•ด์„œ ์–ด๋– ํ•œ ์ฐจ์ด๊ฐ€ ์žˆ๋Š”์ง€ ๋น„๊ตํ•ด๋ณด์ž.

 

lock free (CAS) ๊ตฌํ˜„

์›๋ฆฌ

์šฐ์„  lock free ๊ธฐ๋ฒ• ์ค‘ CAS์— ๋Œ€ํ•ด์„œ ์‚ดํŽด๋ณด์ž.

Compare-and-Swap ์—ฐ์‚ฐ์€ ๋Œ€ํ‘œ์ ์ธ lock-free ๊ธฐ๋ฒ•์ด๋‹ค. ๋ณ€๊ฒฝํ•˜๊ณ ์ž ํ•˜๋Š” ๋ณ€์ˆ˜๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๋ณ€๊ฒฝํ•˜๊ณ ์ž ํ•˜๋Š” ๊ฐ’์„ ๊ณ„์‚ฐํ•œ ๋‹ค์Œ, _(๋ณ€์ˆ˜์˜ ๊ฐ’์ด ๋ณ€ํ•˜์ง€ ์•Š์•˜๋‹ค๋ฉด, ๋ณ€์ˆ˜์˜ ๊ฐ’์— ๊ณ„์‚ฐํ•œ ๊ฐ’ ํ• ๋‹น)_์˜ ๊ณผ์ •์„ ์›์ž์ ์œผ๋กœ ์‹œํ–‰ํ•œ๋‹ค. ๊ฒฐ๊ตญ ํ•˜๋“œ์›จ์–ด ์ƒ์—์„œ๋Š” ์ด ๊ณผ์ •์„ ์›์ž์ ์œผ๋กœ ํ–‰ํ•˜๊ธฐ ์œ„ํ•ด lock signal์„ ๋ฟŒ๋ฆฌ๋Š” ์‹์œผ๋กœ ๊ตฌํ˜„๋œ๋‹ค.

๋จผ์ € ์ด๋ ‡๊ฒŒ node A, B, C๊ฐ€ ์กด์žฌํ•˜๊ณ  A->B->C ์ˆœ์œผ๋กœ ์ด์–ด์ ธ์žˆ๋Š” LinkedList๊ฐ€ ์žˆ๋‹ค๊ณ  ๊ฐ€์ •ํ•˜์ž. ์ด ๋•Œ, pop ์—ฐ์‚ฐ์„ ์ˆ˜ํ–‰ํ•˜์—ฌ node A๋ฅผ ์ œ๊ฑฐํ•˜๋ ค๊ณ  ํ•œ๋‹ค.

๋ณ€๊ฒฝํ•˜๊ณ ์ž ํ•˜๋Š” ๋ณ€์ˆ˜์ธ head๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๋ณ€๊ฒฝํ•˜๊ณ ์ž ํ•˜๋Š” ๊ฐ’ newHead๋ฅผ ๊ณ„์‚ฐํ•œ ๋‹ค์Œ, (head๊ฐ€ oldHead์™€ ๊ฐ™๊ฐ€๋ฉด, head์— newHead ํ• ๋‹น)์˜ ๊ณผ์ •์„ ์ˆ˜ํ–‰ํ•œ๋‹ค.

c++์—์„œ๋Š” CAS ์—ฐ์‚ฐ์‹œ atomic_compare_exchange_weak๋ผ๋Š” ํ•จ์ˆ˜๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค. ์ฝ”๋“œ๋กœ ํ‘œํ˜„ํ•˜์ž๋ฉด ๋‹ค์Œ๊ณผ ๊ฐ™๋‹ค.

Node* CAS(){
    Node* oldHead; Node* newHead;
    do {
        if ((oldHead = head) == nullptr) {
            return nullptr;
        }
        newHead = oldHead->next;
    } while (!atomic_compare_exchange_weak(&head, &oldHead, newHead));
}

์ „์ฒด ์ฝ”๋“œ๋ฅผ ์‚ดํŽด๋ณด์ž.

 

์ฝ”๋“œ

์—ฌ๋Ÿฌ ์Šค๋ ˆ๋“œ๋กœ ๋™์ž‘ํ•˜๋Š” LinkedList์˜ CAS ์—ฐ์‚ฐ์„ ๊ตฌํ˜„ํ•ด๋ณธ ์ฝ”๋“œ์ด๋‹ค.

#include <iostream> 
#include <thread>
#include <vector> 
#include <atomic>
#include <random>
#include <mutex>

using namespace std;

class Node {
public:
    int value;
    Node* next;
    Node() : value(0) { next = NULL; }

    Node(int k_value) {
        next = NULL;
        value = k_value;
    }
};

int node_n;

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

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

class LFStack {
private:
    Node * head;

public:
    void printNodes() {
        int size = 0;
        while (head != nullptr) {
            size++;
            head = head->next;
            if (size > 100000) {
                cout << "size over 100,000"<< endl;
                return;
            }
        }
        cout << size << endl;
    }

    void push(Node* newNode) {
        Node* oldNode;
        do {
            oldNode = head;
            newNode->next = oldNode;
        } while (!atomic_compare_exchange_weak(&head, &oldNode, newNode));
    }

    Node* pop() {
        Node* oldNode; Node* newNode;
        do {
            if ((oldNode = head) == nullptr) {
                return nullptr;
            }
            // head_list์— data๊ฐ€ ๋“ค์–ด์˜ฌ ๋•Œ๊นŒ์ง€ ๊ธฐ๋‹ค๋ฆฐ๋‹ค.
            newNode = oldNode->next;
        } while (!atomic_compare_exchange_weak(&head, &oldNode, newNode));

        oldNode->next = nullptr;
        return oldNode;
    }
};

LFStack* FreeList;
LFStack* HeadList;

void subThread() {
    // random n, m
    int n = dis(gen);
    int m = dis(gen);

    int num = 1000; // while๋ฌธ ๋ฐ˜๋ณต ํšŸ์ˆ˜
    while (num--) {
        for (int i = 0; i < n; i++)
        {
            // free_list์— ์žˆ์œผ๋ฉด head_list์— ์˜ฎ๊ธฐ๋Š” ์ž‘์—…
            Node* tmp = FreeList->pop();
            if (tmp == nullptr)
                continue;
            HeadList->push(tmp);

        }
        for (int i = 0; i < m; i++) {
            // head_list์— ์žˆ์œผ๋ฉด free_list์— ์˜ฎ๊ธฐ๋Š” ์ž‘์—…
            Node* tmp = HeadList->pop();
            if (tmp == nullptr)
                continue;
            FreeList->push(tmp);
        }
    }

}

int main() {
    // free, head list ์ƒ์„ฑ
    FreeList = new LFStack();
    HeadList = new LFStack();

    // ๋…ธ๋“œ ์ƒ์„ฑ ๋ฐ free_list์— ์‚ฝ์ž…
    node_n = 100000; // ๋…ธ๋“œ ๊ฐฏ์ˆ˜
    for (int i = node_n - 1; i >= 0; i--) {
        Node* node = new Node(i);
        FreeList->push(node);
    }

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

    FreeList->printNodes();
    HeadList->printNodes();
    cin >> n;
}

random ๊ตฌํ˜„์€ time seed์„ ์ด์šฉํ•˜๋ฉด ์“ฐ๋ ˆ๋“œ๊ฐ€ ๋งŒ๋“ค์–ด์ง€๋Š” ์‹œ๊ฐ์ด ๊ฑฐ์˜ ๋™์ผํ•˜๊ธฐ ๋•Œ๋ฌธ์— ์Šค๋ ˆ๋“œ ๋ณ„๋กœ ๋ณ€์ˆ˜ ๊ฐ’์ด ๋‹ค๋ฅด๊ฒŒ ๋‚˜์˜ค์ง€ ์•Š์•„์„œ Pseudo-random์ธ disgen์„ ์‚ฌ์šฉํ•˜์˜€๋‹ค.

 

๊ฒฐ๊ณผ

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

์ดํ›„์˜ ์‹œํ–‰์—์„œ๋Š” thread๋Š” 5๊ฐœ, while๋ฌธ ์ตœ์†Œ 10,000ํšŒ๋กœ ์‹œํ–‰ํ•˜์˜€์ง€๋งŒ ์ด ์ฝ”๋“œ๋Š” ์˜ค๋ฅ˜๊ฐ€ ๋งŽ์ด ๋ฐœ์ƒํ•˜๊ธฐ ๋•Œ๋ฌธ์— thread ์ˆ˜์™€ while๋ฌธ ์ˆ˜ํ–‰ํšŸ์ˆ˜๋ฅผ ์ถ•์†Œํ•˜์—ฌ ์‹œํ–‰ํ•˜์˜€๋‹ค.

 

  • ๊ฒฐ๊ณผ
๊ตฌ๋ถ„ free_list ๋…ธ๋“œ ์ˆ˜ head_list ๋…ธ๋“œ ์ˆ˜
์ •์ƒ์ž‘๋™ 97,000 3,000
๋…ธ๋“œ ๊ฐ์†Œ 0 1
๋…ธ๋“œ ์ฆ๊ฐ€ 100,000๊ฐœ ์ด์ƒ 100,000๊ฐœ ์ด์ƒ

<์ •์ƒ ์ž‘๋™ ์‹œ ๊ฒฐ๊ณผ>

97000 / 3000

<๋…ธ๋“œ ๊ฐ์†Œ ์‹œ ๊ฒฐ๊ณผ>

0 / 1

<๋…ธ๋“œ ์ฆ๊ฐ€ ์‹œ ๊ฒฐ๊ณผ>

size over 100,000 / size over 100,000

 

๊ฒฐ๊ณผ ๋ถ„์„

head๊ฐ€ ๊ฐ€๋ฆฌํ‚ค๋Š” ์—ฐ๊ฒฐ๋ฆฌ์ŠคํŠธ๊ฐ€ ๋‹ค์Œ๊ณผ ๊ฐ™์ด ๊ตฌ์„ฑ๋˜์–ด์žˆ๊ณ , ์ฝ”๋“œ์— ๋”ฐ๋ผ tmp๊ฐ€ head๋ฅผ loadํ•˜๊ณ  tmp_next๋Š” tmp๊ฐ€ ๊ฐ€๋ฆฌํ‚ค๋Š” ๋…ธ๋“œ์˜ next_node๋ฅผ ๊ฐ€๋ฆฌํ‚จ๋‹ค๊ณ  ํ•˜์ž. ์œ„์˜ ๊ทธ๋ฆผ๊นŒ์ง€ ์ฝ”๋“œ๋Š” ์ง„ํ–‰๋˜์—ˆ๊ณ , ์ด ๋•Œ ๋‹ค๋ฅธ thread๊ฐ€ ์ ‘๊ทผํ•˜์—ฌ headlist์— ๋Œ€ํ•œ pop => pop => push(0) ์—ฐ์‚ฐ์„ ์ˆ˜ํ–‰ํ•˜์˜€๋‹ค.

๊ทธ๋Ÿฌ๋ฉด ๋‹ค์Œ๊ณผ ๊ฐ™์€ ์ƒํƒœ๊ฐ€ ๋˜๋Š”๋ฐ, CAS์—ฐ์‚ฐ์„ ์‹œํ–‰ํ•˜๋ฉด head์™€ tmp๊ฐ€ ๊ฐ™์œผ๋ฏ€๋กœ tmp_next๋ฅผ head์— ๋Œ€์ž…ํ•˜๊ฒŒ ๋œ๋‹ค.

head์™€ free๊ฐ€ ๊ฐ™์€ ๋…ธ๋“œ๋ฅผ ๊ฐ€๋ฆฌํ‚ค๋ฏ€๋กœ ๋…ธ๋“œ์˜ ์ˆ˜๊ฐ€ 2๋ฐฐ๋˜์–ด ๋…ธ๋“œ๊ฐ€ ์ฆํญ๋˜์–ด ๋‚˜ํƒ€๋‚  ์ˆ˜๋„ ์žˆ์œผ๋ฉฐ, tmp๊ฐ€ ๊ฐ€๋ฆฌํ‚ค๊ณ  ์žˆ๋Š” ๋…ธ๋“œ๋“ค์ด ๋”์ด์ƒ ์ฐธ์กฐ๋˜์ง€ ์•Š์œผ๋ฉด ์‚ฌ๋ผ์ ธ๋ฒ„๋ฆฐ ๊ฒƒ์ด๋ฏ€๋กœ ๋…ธ๋“œ๊ฐ€ ์—†์–ด์งˆ ์ˆ˜๋„ ์žˆ๋‹ค.

 

๊ฒฐ๊ตญ ABA ๋ฌธ์ œ ๋•Œ๋ฌธ์— ๋…ธ๋“œ ๊ฐ์†Œ, ์ฆ๊ฐ€ ํ˜„์ƒ์ด ๋‚˜ํƒ€๋‚˜๋Š” ๊ฒƒ์ด๋‹ค.

 

ABA ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•ด๋ณด์ž!

 

=> ABA ๋ฌธ์ œํ•ด๊ฒฐ์€ ๋‹ค์Œ ํฌ์ŠคํŒ…์—

๋ฐ˜์‘ํ˜•