์์
๐งต ์ฃผ์
C++์์ SpinLock์ ์ง์ ๊ตฌํํ๋ฉฐ ๋ฉํฐ์ค๋ ๋ ๋๊ธฐํ, atomic ์ฐ์ฐ, compare_exchange_strong ํจ์์ ์๋ฏธ๋ฅผ ์ดํดํ๊ณ ์ค์ ์ฝ๋๋ก ํ์ฉํ๋ ๋ฒ ํ์ต
๐ ๊ฐ๋
โ
SpinLock์ด๋?
- SpinLock์ ๋ฝ์ด ํ๋ฆด ๋๊น์ง ๋ฃจํ๋ฅผ ๋๋ฉฐ ๊ธฐ๋ค๋ฆฌ๋ ๋ฝ์ด๋ค.
- OS์๊ฒ CPU๋ฅผ ๋๊ธฐ์ง ์๊ณ ๊ณ์ CPU๋ฅผ ์ ์ ํ ์ฑ ๋ฐ์ ๋๊ธฐ(busy waiting)๋ฅผ ํ๋ฉฐ ๋ฝ์ ์๋ํ๋ค.
- ๋ฐ๋ผ์ lock/unlock ์ฌ์ดํด์ด ๋งค์ฐ ์งง๊ณ ์ถฉ๋์ด ์ ์ ์ํฉ์์ ํจ๊ณผ์ ์ด๋ค.
- ๋ฐ๋ฉด, ๋ฝ์ด ์ค๋ ๊ฑธ๋ฆฌ๋ฉด CPU๋ฅผ ๋ฌด์๋ฏธํ๊ฒ ์๋ชจํ๋ฏ๋ก ๋งค์ฐ ๋นํจ์จ์ ์ผ ์ ์๋ค.
โ
SpinLock์ ์ฅ๋จ์
| ํญ๋ชฉ | ์ฅ์ | ๋จ์ |
|---|
| CPU ์ฌ์ฉ | ์ปจํ
์คํธ ์ค์์นญ์ด ์์ผ๋ฏ๋ก ๋น ๋ฆ | CPU ์ ์ ์จ์ด ๋งค์ฐ ๋์ |
| ๋ฝ ๋๊ธฐ | ๋ฝ์ด ๊ธ๋ฐฉ ํ๋ฆด ๋ ์ ํฉ | ์ค๋ ๊ฑธ๋ฆฌ๋ฉด ์ ์ฒด ์์คํ
์ฑ๋ฅ ํ๋ฝ |
| ๊ตฌ์กฐ | ๊ฐ๋จํ๊ณ lightweight | ๋๊ธฐ ์ค ๋ค๋ฅธ ์์
๋ถ๊ฐ |
โ
์ ๋ฉด์ ์์ ์์ฃผ ๋์ฌ๊น?
- SpinLock์ ๊ตฌํํด๋ณด๋ผ๊ณ ํ๋ ์ด์ ๋ ๋ฉํฐ์ค๋ ๋ ๋๊ธฐํ ๊ฐ๋
, atomic ์ฐ์ฐ ์ดํด๋, ๋๊ธฐํ ์ค๋ฅ์ ๋ํ ๋๋ฒ๊น
๋ฅ๋ ฅ์ ๊ฒ์ฆํ ์ ์๊ธฐ ๋๋ฌธ.
- ํนํ compare_exchange_strong(CAS ์ฐ์ฐ)์ ๋ํ ์ดํด ์ฌ๋ถ๋ฅผ ๋ณด๊ธฐ ์ํจ.
๐ ์ฉ์ด ์ ๋ฆฌ
| ์ฉ์ด | ์ค๋ช
|
|---|
| SpinLock | ๋ฝ์ด ํ๋ฆด ๋๊น์ง ๋ฃจํ๋ฅผ ๋๋ฉฐ ๊ธฐ๋ค๋ฆฌ๋ ๋ฝ |
| atomic | ๋ฉํฐ์ค๋ ๋ ํ๊ฒฝ์์ ์์์ฑ ๋ณด์ฅ์ ์ํ ํ์
|
| compare_exchange_strong | ์กฐ๊ฑด์ด ๋ง์ผ๋ฉด ๊ฐ์ ๋ฐ๊พธ๋ CAS ๋ฐฉ์ ํจ์ |
| volatile | ์ปดํ์ผ๋ฌ ์ต์ ํ๋ฅผ ๋ง๋ ํค์๋ (ํ์ง๋ง ๋๊ธฐํ ๋ณด์ฅ X) |
| context switching | ์ค๋ ๋ ์ ํ ์ ๋ ์ง์คํฐ ๋ฑ ์ํ ์ ์ฅ/๋ณต์ ๋น์ฉ |
๐ป ์ฝ๋ ๋ถ์
โ ์๋ชป๋ SpinLock ๊ตฌํ
class SpinLock
{
public:
void lock()
{
while (_locked) { }
_locked = true;
}
void unlock()
{
_locked = false;
}
private:
bool _locked = false;
};
๋ฌธ์ ์
_locked๋ bool๋ก ์ ์ธ๋์ด ์์์ฑ ๋ณด์ฅ์ด ์ ๋จ โ Race Condition ๋ฐ์
while (_locked)๊ณผ _locked = true๊ฐ ๋ถ๋ฆฌ๋ ์ฐ์ฐ โ ๋๊ธฐํ ์คํจ ๊ฐ๋ฅ
- ์ปดํ์ผ๋ฌ๊ฐ ์ต์ ํ๋ฅผ ํด๋ฒ๋ฆด ์๋ ์์ โ ์์ธก ๋ถ๊ฐํ ๋์
โ
๊ฐ์ ๋ SpinLock ๊ตฌํ (atomic + CAS)
class SpinLock
{
private:
std::atomic<bool> _locked = false;
public:
void lock()
{
bool expected = false;
bool desired = true;
while (_locked.compare_exchange_strong(expected, desired) == false)
{
expected = false;
}
}
void unlock()
{
_locked.store(false);
}
};
ํต์ฌ ์ค๋ช
compare_exchange_strong(expected, desired)๋ ์๋์ ๊ฐ์ด ๋์:
if (_locked == expected) {
_locked = desired;
return true;
} else {
expected = _locked;
return false;
}
- expected๋ ์ฐธ์กฐ๋ก ์ ๋ฌ๋๋ฉฐ, ์คํจ ์ ๋ด๋ถ์์ ๊ฐ์ด ๋ณ๊ฒฝ๋๋ฏ๋ก ๋ค์
false๋ก ์ด๊ธฐํํด์ผ ๋ฃจํ๊ฐ ์ ์ ๋์ํจ
โ
ํ
์คํธ ์ฝ๋
int32 sum = 0;
SpinLock spinLock;
void Add()
{
for (int32 i = 0; i < 100000; i++)
{
std::lock_guard<SpinLock> guard(spinLock);
sum++;
}
}
void Sub()
{
for (int32 i = 0; i < 100000; i++)
{
std::lock_guard<SpinLock> guard(spinLock);
sum--;
}
}
int main()
{
std::thread t1(Add);
std::thread t2(Sub);
t1.join();
t2.join();
std::cout << sum << std::endl;
}
ํต์ฌ ํฌ์ธํธ
std::lock_guard๋ lock()/unlock() ์ธํฐํ์ด์ค๋ง ์์ผ๋ฉด ๋์ํจ
- RAII ํจํด์ผ๋ก ์์ธ๊ฐ ๋ฐ์ํด๋ lock ์๋ ํด์ โ ์์ ํ ๊ตฌ์กฐ
๐ฏ ํต์ฌ
SpinLock์ ์งง์ ๋ฝ ์ํฉ์์ ์ปจํ
์คํธ ์ค์์นญ์ ์ค์ด๊ณ ๋น ๋ฅด๊ฒ ๋๊ธฐํํ ์ ์๋ค.
- ๋จ์
bool ๋ณ์๋ก๋ ์ ๋ ์์ ํ ๋ฝ์ด ๋ถ๊ฐ๋ฅํ๋ค โ ๋ฐ๋์ std::atomic ์ฌ์ฉ.
compare_exchange_strong ํจ์์ expected ์ธ์๋ ์ฐธ์กฐ์ด๋ฏ๋ก, ๋ฃจํ๋ง๋ค expected = false๋ก ์ด๊ธฐํํด์ค์ผ ํ๋ค.
volatile์ ์ต์ ํ๋ง ๋ง์ ๋ฟ, ๋๊ธฐํ๋ ์์์ฑ์ ๋ณด์ฅํ์ง ์์.
- ์ค์ ์์๋ ๋ฝ์ด ์งง์ ๊ฒฝ์ฐ์๋ง
SpinLock์ ์ฌ์ฉํ๊ณ , ๊ทธ๋ ์ง ์์ผ๋ฉด mutex, condition_variable ๋ฑ์ ๋์ฒด ๋๊ตฌ ์ฌ์ฉ์ด ๋ฐ๋์งํ๋ค.