19.4 레이스 컨디션(Race Condition) std::atomic, std::scoped_lock
같은 공간을 사용하기 때문에 생기는 문제점을 해결하는 방법을 알아보자.
#include <iostream>
#include <thread>
#include <chrono>
#include <atomic>
#include <mutex>
using namespace std;
int main()
{
int shared_memory(0);
auto count_func = [&](){
for (int i=0;i<1000;++i)
{
this_thread::sleep_for(chrono::milliseconds(1));
shared_memory++;
}
};
thread t1 = thread(count_func);
t1.join();
cout << "After" << "\n";
cout << shared_memory << "\n"; // 1000
return 0;
}
#include <iostream>
#include <thread>
#include <chrono>
#include <atomic>
#include <mutex>
using namespace std;
int main()
{
int shared_memory(0);
auto count_func = [&](){
for (int i=0;i<1000;++i)
{
this_thread::sleep_for(chrono::milliseconds(1));
shared_memory++;
}
};
thread t1 = thread(count_func);
thread t2 = thread(count_func);
t1.join();
t2.join();
cout << "After" << "\n";
cout << shared_memory << "\n"; // 2000이 나오지 않고 1962밖에 안나옴.
// 병렬 처리 프로그래밍 할 때 어려운 점.
// shared_memory를 갖고 와서 1을 더한 후에 결과값을 다시 덮어쓴다.
// cpu로 읽어들인 사이에 thread2가 읽어서 더하는 경우에 문제가 생김.
return 0;
}
#include <iostream>
#include <thread>
#include <chrono>
#include <atomic>
#include <mutex>
using namespace std;
mutex mtx;
int main()
{
atomic<int> shared_memory(0);
auto count_func = [&](){
for (int i=0;i<1000;++i)
{
this_thread::sleep_for(chrono::milliseconds(1));
// mtx.lock(); , mtx.unlock(); 으로도 처리 가능.
shared_memory++; // int의 증감연산자가 아니라 atomic에 연산자 오버로딩이 되어있는 것.
// shared_memory.fetch_add(1); // 이렇게도 사용 가능.
// 그냥 int의 ++보단 느리다.
}
};
thread t1 = thread(count_func);
thread t2 = thread(count_func);
t1.join();
t2.join();
cout << "After" << "\n";
cout << shared_memory << "\n"; // 2000이 나옴.
return 0;
}
mutex lock_gurad
#include <iostream>
#include <thread>
#include <chrono>
#include <atomic>
#include <mutex>
using namespace std;
mutex mtx;
int main()
{
int shared_memory(0);
auto count_func = [&](){
for (int i=0;i<1000;++i)
{
this_thread::sleep_for(chrono::milliseconds(1));
// mtx.lock(); , mtx.unlock(); 으로도 처리 가능.
std::lock_gurad lock(mtx); // 이렇게 쓰는 것을 권장.
// std::scoped_lock lock(mtx); //조금 더 권장하는 방식. 이 scope를 벗어나는 순간 lock이 풀림.
shared_memory++;
}
};
thread t1 = thread(count_func);
thread t2 = thread(count_func);
t1.join();
t2.join();
cout << "After" << "\n";
cout << shared_memory << "\n"; // 2000이 나옴.
return 0;
}
#include <iostream>
#include <thread>
#include <chrono>
#include <atomic>
#include <mutex>
using namespace std;
int main()
{
int shared_memory(0);
auto count_func = [&](){
for (int i=0;i<1000;++i)
{
//this_thread::sleep_for(chrono::milliseconds(1));
shared_memory++;
}
};
thread t1 = thread(count_func);
thread t2 = thread(count_func);
t1.join();
t2.join();
cout << "After" << "\n";
cout << shared_memory << "\n"; // millieseconds를 지우면 2000이 나옴.
// 너무 빨리 처리해버려서 t1->t2 순으로 진행되기 때문에 문제가 생기지 않은 것처럼 보임.
return 0;
}