- Data Race๋ก ์ธํ ์ค๋์
1) Data Race๋ฅผ ์์ ์ → Lock/Unlock → ์ฑ๋ฅ ์ ํ → Data Race๋ฅผ ์ค์ด์ → ์๊ณ ๋ฆฌ์ฆ ์ฌ ์์ฑ → ๋๋ฌด ์ด๋ ต๋ค
2) Data Race๋ฅผ ๊ณ ๋ คํ ํ๋ก๊ทธ๋๋ฐ์ ํ์ → ???
- ์ปดํ์ผ๋ฌ ์ฃผ์์
: Data Race๊ฐ ์๋ ์ค ์๊ณ ์๊ณ , ์คํ ๊ฒฐ๊ณผ๋ฅผ ์์ธกํ ์ ์์ผ๋ฉด ๋ฌธ์ ๋ฅผ ํผํ ์ ์๋๋ก ํ๋ก๊ทธ๋๋ฐ ํ ์ ์์ง ์์๊น?
์๋ ์ฝ๋๋ฅผ ๋๋ ค๋ณด๋๋ก ํ์.
bool g_ready = false;
int g_data = 0;
std::mutex srm;
void Reciver()
{
while (false == g_ready)
std::cout << "Recv: " << g_data << std::endl;
}
void Sender()
{
std::this_thread::sleep_for(std::chrono::milliseconds(10));
g_data = 999;
g_ready = true;
}
int main()
{
std::thread R{ Reciver };
std::thread S{ Sender };
R.join();
S.join();
}
→ ๊ฒฐ๊ณผ ๊ฐ : Ok(Debug๋ชจ๋), ์ํฐ๋ฆฌ(Release ๋ชจ๋), why???
→ ์ปดํ์ผ๋ฌ ์ฌ๊ธฐ(๋ณ์ g_ready๋ฅผ ๋งค ๋ฐ๋ณต๋ฌธ๋ง๋ค ํ์ธํ์ง ์์ & ์ต์ ํ ํ๋ค๊ณ ์์๋ฅผ ๋ฐ๊ฟ๋ฒ๋ฆผ). ์๋ฅผ ํผํ๋ ค๋ฉด?
1) lock/unlock์ ์ฌ์ฉํ๋ค.
2) volatile์ ์ฌ์ฉํ๋ฉด ๋๋ค. → ์๋ง ์ฐ๋ฉด ํด๊ฒฐ ์๋จ
• ๋ฐ๋์ ๋ฉ๋ชจ๋ฆฌ๋ฅผ ์ฝ๊ณ ์ด๋ค.
• ๋ณ์๋ฅผ ๋ ์ง์คํฐ์ ํ ๋นํ์ง ์๋๋ค.
• ์ฝ๊ณ ์ฐ๋ ์์๋ฅผ ์งํจ๋ค.
→ ์ด์ ๋ธ๋ฆฌ๋ฅผ ๋ชจ๋ฅด๋ฉด VS์ ์ฌ๊ธฐ๋ฅผ ์ ์ ์๋ค.
→ Lock์ ์ฌ์ฉํด๋ณด์
bool g_ready = false;
int g_data = 0;
std::mutex srm;
void Reciver()
{
srm.lock();
while (false == g_ready) {
srm.unlock();
srm.lock();
}
std::cout << "Recv: " << g_data << std::endl;
srm.unlock();
}
void Sender()
{
std::this_thread::sleep_for(std::chrono::milliseconds(10));
srm.lock();
g_data = 999;
g_ready = true;
srm.unlock();
}
int main()
{
std::thread R{ Reciver };
std::thread S{ Sender };
R.join();
S.join();
}
→ ์ ์์ ์ธ ๊ฐ์ด ๋์จ๋ค
→ volatile์ ์ ์ฉํจ์๋ ์ด์ํ ๊ฐ์ด ๋์ฌ ์ ์์. ๋์ฒด ์? : ์ด์ ๋ธ๋ฆฌ์์ ํ์ธํด๋ ๋ชจ๋ฅด๊ฒ ์.
- volatile ์ฌ์ฉ๋ฒ (๊ณ ์์ด๋ค...)
: volatile int* a; → a* = 1; (volatile ์ ์ฉ, ํ๋ก๊ทธ๋จ ๊ทธ๋๋ก ์ปดํ์ผ) a = b; (์ปดํ์ผ ์ต์ ํ ๋์)
: ํฌ์ธํฐ๊ฐ ๊ฐ๋ฆฌํค๋ ๋ฐ์ดํฐ๋ ์ต์ ํํ์ง ์๊ณ ๊ทธ๋๋ก ์ปดํ์ผ ๋๋, ํฌ์ธํฐ ์์ฒด ๊ฐ์ ๋ณ๊ฒฝ์ ํฌํจ ๋์ ์๋
: int* volatile a; → a* = 1; (์ปดํ์ผ ์ต์ ํ ๋์) a = b; (volatile ์ ์ฉ, ํ๋ก๊ทธ๋จ ๊ทธ๋๋ก ์ปดํ์ผ)
: ํฌ์ธํฐ ์์ฒด(๊ฐ๋ฆฌํค๋ ์ฃผ์ ๊ฐ)๋ฅผ ๋ณ๊ฒฝํ๋ ๋ชจ๋ ์์ ์ ์ต์ ํํ์ง ์์ผ๋, ํฌ์ธํฐ๊ฐ ๊ฐ๋ฆฌํค๋ ๊ฐ์ ์ต์ ํ ๋์
: ์ฌ๋ฌ ๊ฐ์ ์ฐ๋ ๋๊ฐ ๊ณต์ ํ๋ ๋ณ์๋ volatile์ ์ฌ์ฉํด์ผ ํ๋๋ฐ, ์ด๋ ์์น๋ฅผ ์ ๋๋ก ์จ์ค์ผ ํ๋ค.
: volatile์ ์ฌ์ฉํ๋ฉด ์ปดํ์ผ๋ฌ๋ ํ๋ก๊ทธ๋๋จธ๊ฐ ์ง์ํ ๋๋ก ๋ฉ๋ชจ๋ฆฌ์ ์ ๊ทผํจ (์์ผ๋ฉด ์ปดํ์ผ๋ฌ๋ ์ฑ๊ธ์ฐ๋ ๋ ๊ธฐ์ค์ผ๋ก ์ต์ ํ)
ํ์ฌ๋ skipํ๋, CPU๋ volatile์ ๋ชจ๋ฅธ๋ค
(์ถ์ธก์ ์ด์ ๋ธ๋ฆฌ ๋ ๋ฒจ์์ volatile ํค์๋์ ์กด์ฌ๋ฅผ ์ธ์ํ์ง ์๋๋ค๋ ๊ฒ์ด์ง ์์๊น....)
- CPU ์ฃผ์์ (์ํธ๋ฐฐ์ ๊ตฌํ, ๋ฉ๋ชจ๋ฆฌ ์ผ๊ด์ฑ ๋ฌธ์ )
: ์ํธ๋ฐฐ์ (mutual exclusion) - ํ๋์ ์์์ ์ฌ๋ฌ ์ค๋ ๋์์ ๋์์ ์ฌ์ฉํด์ ์๊ธฐ๋ ๊ฒฝ์ฐ, ๊ณต์ ์์์ ์ ๋ฐ์ดํธ ํ๋ ๋ถ๋ถ์ ํ ๋ฒ์ ํ๋์ ์ฐ๋ ๋๋ง ์คํํ ์ ์๋๋ก ํจ
: ์๊ณ์์ญ(Critical Section) - ํ๋ก๊ทธ๋จ ์ค ์ํธ๋ฐฐ์ ๋ก ๋ณดํธ ๋ฐ๊ณ ์๋ ๊ตฌ๊ฐ, ์ค์ง ํ๋์ ์ฐ๋ ๋๋ง ์คํ ๊ฐ๋ฅ
: ๋ค๋ฅธ ์ฐ๋ ๋๊ฐ Lock์ ํต๊ณผํ๊ณ Unlock์ ํ๊ธฐ ์ → Unlock์ ํ ๋๊น์ง ํ๋ก๊ทธ๋จ์ ์คํ ๋๊ธฐ (๋ฉ์ถค)
1) ํผํฐ์จ ์๊ณ ๋ฆฌ์ฆ
: 2๊ฐ์ ์ฐ๋ ๋ ์ฌ์ด์ Lock๊ณผ Unlock์ ๊ตฌํํ๋ ์๊ณ ๋ฆฌ์ฆ
: ๋งค๊ฐ๋ณ์๋ก ์ฐ๋ ๋ ID ์ ๋ฌ ๋ฐ์ผ๋ฉฐ ๊ฐ์ 0๊ณผ 1์ด๋ผ๊ณ ๊ฐ์ .
volatile int victim = 0;
volatile bool flag[2] = { false, false };
int sum;
std::mutex m1;
void Lock(int myID)
{
int other = 1 - myID;
flag[myID] = true;
std::atomic_thread_fence(std::memory_order_seq_cst);
victim = myID;
while (flag[other] && victim == myID) {}
}
void Unlock(int myID)
{
flag[myID] = false;
}
void thread_work(int thid)
{
for (auto i = 0; i < 25000000; ++i) {
Lock(thid);
sum = sum + 2;
Unlock(thid);
}
}
int main()
{
std::thread t1{ thread_work, 0 };
std::thread t2{ thread_work, 1 };
t1.join();
t2.join();
std::cout << "sum = " << sum << std::endl;
}
: volatile๋ก ์ ์ธ → ๋งค ๋ฐ๋ณต๋ฌธ๋ง๋ค ๋ณ์๋ฅผ ํ์ธํ ์ ์๊ฒ ํจ & std::atomic_thread_fence(std::memory_order_seq_cst)๋ฅผ ์ฌ์ฉํ์ฌ CPU๊ฐ ์์๋ฅผ ๋ฐ๊พธ๋ ๊ฒ(Out-of-Other)์ ๋ง์
: Out-of-other - CPU๊ฐ ์ต์ ํ๋ฅผ ํตํด ์๋ก ์์กด์ฑ์ด ์๋ค ์๊ฐํ๋ฉด ๋ง์๋๋ก ๋ฐ๊พธ๋ ํ์
: ๊ฒฐ๊ณผ → ๋น๋ฒํ ๋ฉ๋ชจ๋ฆฌ ์ฐธ์กฐ๋ก ์ธํ ์ฑ๋ฅ ๋ฌธ์ & ์ค์ ์ปดํจํฐ์์์ ์ค๋์(์ค๋ฝ๊ฐ๋ฝ...) (๋ค์ ์ฅ์์ ์์ธํ ๋ค๋ฃธ)
: ํด๊ฒฐ์ฑ → mutex ์ฌ์ฉ - ํธ๋ฆฌํ ๊ธฐ๋ฅ ์ ๊ณตํ๋ ์ค๋ฒํค๋๋ก ์ธํ ์ฑ๋ฅ ๋ฌธ์ ๋ฐ์
2) ๋นต์ง ์๊ณ ๋ฆฌ์ฆ ( ๊ณผ์ 1 )
: N๊ฐ์ ์ฐ๋ ๋์์์ ๋๊ธฐํ ๊ตฌํ
constexpr int MAX = 8;
volatile int sum = 0;
volatile int label[MAX];
volatile bool flag[MAX];
mutex mtx;
void Thread_bakery_lock(int id, const int num_thread) {
// flag = true์ด๋ฉด ๋ค๋ฅธ ์ค๋ ๋ ์ ๊ทผ X
flag[id] = true;
// ํ์ฌ ์ค๋ ๋์ label ์ค์ , ๊ฐ์ฅ ํฐ ๊ฐ์ + 1 ํ ๊ฐ์ผ๋ก ์ค์ ํด ์ฐ์ ์์ ๋์
label[id] = 1 + *max_element(label, label + num_thread);
// flag = false, ๋ค๋ฅธ ์ค๋ ๋ ์ ๊ทผ ๊ฐ๋ฅ
flag[id] = false;
for (int other = 0; other < num_thread; ++other) { // ์ค๋ ๋์ ๋ํด ๋ฐ๋ณต
while (flag[other]) {} // ๋ค๋ฅธ ์ค๋ ๋์ flag๊ฐ true์ธ ๋์ ๋๊ธฐ
// ๋ค๋ฅธ ์ค๋ ๋์ label์ด 0์ด ์๋๊ณ , ๋ค๋ฅธ ์ค๋ ๋์ label์ด ํ์ฌ ์ค๋ ๋์ label๋ณด๋ค ์๊ฑฐ๋ ๊ฐ์ ๊ฒฝ์ฐ
// ๋๋ ๋ label์ด ๊ฐ์ ๊ฒฝ์ฐ ๋ค๋ฅธ ์ค๋ ๋์ id๊ฐ ๋ ์์ ๊ฒฝ์ฐ์ ๋๊ธฐ ( ์ฐ์ ์์ ๋์ ๊ฒ๋ถํฐ ์ง์
)
while (label[other] != 0 && (label[other] < label[id] || (label[other] == label[id] && other < id))) {}
}
}
void Thread_bakery_unLock(int id) {
// ๋ฝ ํด์
label[id] = 0;
}
void Thread_work_bakery(int id, const int num_threads) {
for (auto i = 0; i < 50000000 / num_threads; ++i) {
Thread_bakery_lock(id, num_threads);
sum += 2;
Thread_bakery_unLock(id);
}
}
void Thread_work_mutex(const int num_threads) {
for (auto i = 0; i < 50000000 / num_threads; ++i) {
mtx.lock();
sum += 2;
mtx.unlock();
}
}
void Thread_work_noLock(const int num_threads) {
for (auto i = 0; i < 50000000 / num_threads; ++i) {
sum += 2;
}
}
int main() {
vector<int> num_threads_list = { 1, 2, 4, 8 };
// Bakery ์ฌ์ฉ
cout << ">> Bakery Algorithm" << endl;
for (int num_threads : num_threads_list) {
sum = 0;
auto start_t = high_resolution_clock::now();
vector<thread> threads;
for (int i = 0; i < num_threads; ++i)
threads.emplace_back(Thread_work_bakery, i, num_threads);
for (auto& th : threads)
th.join();
auto exec_t = high_resolution_clock::now() - start_t;
auto ms = duration_cast<milliseconds>(exec_t).count();
cout << "Thread : " << num_threads << ", sum = " << sum << ", Time = " << ms << "ms" << endl;
}
// Mutex ์ฌ์ฉ
cout << ">> Mutex ์ฌ์ฉ" << endl;
for (int num_threads : num_threads_list) {
sum = 0;
auto start_t = high_resolution_clock::now();
vector<thread> threads;
for (int i = 0; i < num_threads; ++i)
threads.emplace_back(Thread_work_mutex, num_threads);
for (auto& th : threads)
th.join();
auto exec_t = high_resolution_clock::now() - start_t;
auto ms = duration_cast<milliseconds>(exec_t).count();
cout << "Thread : " << num_threads << ", sum = " << sum << ", Time = " << ms << "ms" << endl;
}
// No Lock
cout << ">> No Lock" << endl;
for (int num_threads : num_threads_list) {
sum = 0;
auto start_t = high_resolution_clock::now();
vector<thread> threads;
for (int i = 0; i < num_threads; ++i)
threads.emplace_back(Thread_work_noLock, num_threads);
for (auto& th : threads)
th.join();
auto exec_t = high_resolution_clock::now() - start_t;
auto ms = duration_cast<milliseconds>(exec_t).count();
cout << "Thread : " << num_threads << ", sum = " << sum << ", Time = " << ms << "ms" << endl;
}
}
'๐ค Study > MultiThread' ์นดํ ๊ณ ๋ฆฌ์ ๋ค๋ฅธ ๊ธ
[05์ฃผ์ฐจ] ๋๊ธฐํ ์ฐ์ฐ & CAS (0) | 2024.04.24 |
---|---|
[04์ฃผ์ฐจ] ๋ฉ๋ชจ๋ฆฌ ์ผ๊ด์ฑ (0) | 2024.04.18 |
[02์ฃผ์ฐจ] ๋ฉํฐ์ฝ์ด HW & DataRace (0) | 2024.04.18 |
[01์ฃผ์ฐจB] ๋ฉํฐ์ฝ์ด HW & ๋ฉํฐ์ฐ๋ ๋ํ๋ก๊ทธ๋๋ฐ ์์ (0) | 2024.03.28 |
[01์ฃผ์ฐจA] ๋ฉํฐ์ฐ๋ ๋ ํ๋ก๊ทธ๋๋ฐ ์๊ฐ (0) | 2024.03.12 |