๋ชฉ์ฐจ
- ๋ฌธ์ ์ ์
- lock free ๊ตฌํ
- ABA ํด๊ฒฐ
- intํ ๊ตฌํ(+ Hazard pointer)
- Counter
- ๊ทธ ์ธ์ ๋ฐฉ๋ฒ๋ค
- 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 ๋ฌธ์ ํด๊ฒฐ์ ๋ค์ ํฌ์คํ ์
Comment