티스토리 뷰

사이트 : https://modoocode.com/271

 

씹어먹는 C++ - <15 - 3. C++ memory order 와 atomic 객체>

 

modoocode.com

 

Atomic - CAS(Compare_And_Swap) 의사 코드 (한줄로 실행)

if(_locked == expected){
    exptected = _locked;
    _locked = desired;
    return true;	
}
else{
    expected = _locked;
    return false;
}

이 코드의 핵심은 SpinLock입니다. SpinLock이란, while로 반복문을 돌면서 내가 Lock을 잡을 수 있는지 확인하는 방식의 Lock이다. Lock을 걸때는 Atomic의 compare_exchange_strong을 사용하여 _locked가 false, 즉 아무도 Lock을 잡지 않은 상태일때 while을 풀고 lock을 잡는다. 이후에는 unlock()을 사용하여 다시 false 상태로 바꿔주어야 한다.

 

1. std::atomic<bool> 버전의 SpinLock -> CAS방식을 사용함

class SpinLock {
public:
	void lock() {
		bool expected = false;
		bool desired = true;

		while (_locked.compare_exchange_strong(expected, desired) == false) 
		{
			expected = false;
		}
	}
	void unlock() {
		_locked.store(false);
	}

private:
	std::atomic<bool> _locked = false;
};

 

2. std::atomic_flag 버전의 SpinLock

2-1 ) test_and_set()을 사용하면 _locked가 true로 바뀌면서 lock을 잠그게 됨.

2-2 ) clear()을 사용하면 _locked가 false로 바뀌게 되서 lock을 풀게 됨.

class SpinLock {
public:
	void lock() {
		while (_locked.test_and_set()) {}
	}

	void unlock() {
		_locked.clear();
	}

private:
	std::atomic_flag _locked = ATOMIC_FLAG_INIT;
};

 

 

사용 방식

더보기
#include <chrono> // std::chrono::miliseconds
#include <condition_variable> // std::condition_variable
#include <iostream>
#include <mutex>
#include <queue>
#include <string>
#include <thread>
#include <vector>
#include <atomic>

SpinLock spinLock;

void producer(std::queue<std::string>*downloaded_pages, int index) {
	for (int i = 0; i < 5; i++) {
		std::this_thread::sleep_for(std::chrono::milliseconds(100 * index));
		std::string content = "웹사이트 : " + std::to_string(i) + " from thread(" + std::to_string(index) + ")\n";

		//Lock
		spinLock.lock();
		downloaded_pages->push(content);
		spinLock.unlock();
	}
}

void consumer(std::queue<std::string>* downloaded_pages, int* num_processed) {
	while (*num_processed < 25) {
    	//Lock
		spinLock.lock();
		if(downloaded_pages->empty()){
			spinLock.unlock();
			continue;
		}
		if (*num_processed == 25) {
			spinLock.unlock();
			return;
		}
		std::string content = downloaded_pages->front();
		downloaded_pages->pop();
		(*num_processed)++;
		std::cout << content;
		spinLock.unlock();
		std::this_thread::sleep_for(std::chrono::milliseconds(80));
	}
}
int main() {
	std::queue<std::string> downloaded_pages;
	std::mutex m;
	std::vector<std::thread> producers;
	for (int i = 0; i < 5; i++) {
		producers.push_back(std::thread(producer, &downloaded_pages, i + 1));
	}
	int num_processed = 0;
	std::vector<std::thread> consumers;
	for (int i = 0; i < 3; i++) {
		consumers.push_back(std::thread(consumer, &downloaded_pages, &num_processed));
	}
	for (int i = 0; i < 5; i++) {
		producers[i].join();
	}
	for (int i = 0; i < 3; i++) {
		consumers[i].join();
	}
}
댓글
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2024/05   »
1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30 31
글 보관함