League Of Legends - Link Select [03์ฃผ์ฐจ] ๋ฉ€ํ‹ฐ์“ฐ๋ ˆ๋“œ ํ”„๋กœ๊ทธ๋ž˜๋ฐ ์ฃผ์˜์‚ฌํ•ญ & ์ƒํ˜ธ๋ฐฐ์ œ ์•Œ๊ณ ๋ฆฌ์ฆ˜
๋ณธ๋ฌธ ๋ฐ”๋กœ๊ฐ€๊ธฐ
๐Ÿค“ Study/MultiThread

[03์ฃผ์ฐจ] ๋ฉ€ํ‹ฐ์“ฐ๋ ˆ๋“œ ํ”„๋กœ๊ทธ๋ž˜๋ฐ ์ฃผ์˜์‚ฌํ•ญ & ์ƒํ˜ธ๋ฐฐ์ œ ์•Œ๊ณ ๋ฆฌ์ฆ˜

by GAMEMING 2024. 4. 18.
728x90

 

- 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();
}

 

release ๋ชจ๋“œ

 

๊ฒฐ๊ณผ ๊ฐ’ : 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; (์ปดํŒŒ์ผ ์ตœ์ ํ™” ๋Œ€์ƒ)

 : ํฌ์ธํ„ฐ๊ฐ€ ๊ฐ€๋ฆฌํ‚ค๋Š” ๋ฐ์ดํ„ฐ๋Š” ์ตœ์ ํ™”ํ•˜์ง€ ์•Š๊ณ  ๊ทธ๋Œ€๋กœ ์ปดํŒŒ์ผ ๋˜๋‚˜, ํฌ์ธํ„ฐ ์ž์ฒด ๊ฐ’์˜ ๋ณ€๊ฒฝ์€ ํฌํ•จ ๋Œ€์ƒ ์•„๋‹˜ 

volatile int* a

 

 

 : int* volatile a; a* = 1; (์ปดํŒŒ์ผ ์ตœ์ ํ™” ๋Œ€์ƒa = b; (volatile ์ ์šฉ, ํ”„๋กœ๊ทธ๋žจ ๊ทธ๋Œ€๋กœ ์ปดํŒŒ์ผ)

 : ํฌ์ธํ„ฐ ์ž์ฒด(๊ฐ€๋ฆฌํ‚ค๋Š” ์ฃผ์†Œ ๊ฐ’)๋ฅผ ๋ณ€๊ฒฝํ•˜๋Š” ๋ชจ๋“  ์ž‘์ ‘์— ์ตœ์ ํ™”ํ•˜์ง€ ์•Š์œผ๋‚˜, ํฌ์ธํ„ฐ๊ฐ€ ๊ฐ€๋ฆฌํ‚ค๋Š” ๊ฐ’์€ ์ตœ์ ํ™” ๋Œ€์ƒ

int* volatile a

 

 

 : ์—ฌ๋Ÿฌ ๊ฐœ์˜ ์“ฐ๋ ˆ๋“œ๊ฐ€ ๊ณต์œ ํ•˜๋Š” ๋ณ€์ˆ˜๋Š” 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;
    }
}