스레드 동기화
#include <iostream>
#include <thread>
#include <chrono>
#include <string_view>
using namespace std::literals;
void delay() { std::this_thread::sleep_for(20ms); }
void foo(std::string_view name)
{
int x = 0; // 지역변수는 스레드에 안전!
static int x = 0; // 전역변수라면? 스레드에 안전하지 못하다
for(int i = 0; i < 10; i++)
{
x = 100; delay();
x = x + 1; delay();
std::cout << name << " : " << x << std::endl; delay();
}
}
int main()
{
std::thread t1(foo, "A");
std::thread t2(foo, "\tB");
t1.join();
t2.join();
}
std::mutex m;
// ...
void foo(std::string_view name)
{
static int x = 0;
for(int i = 0; i < 10; i++)
{
m.lock();
x = 100; delay();
x = x + 1; delay();
std::cout << name << " : " << x << std::endl; delay();
m.unlock();
}
}
// ...
mutex 상세
- mutex종류
std::mutex
std::timed_mutex
std::recursive_mutex
std::recursive_timed_mutex
std::shared_mutex
(C++17)std::shared_timed_mutex
(C++17)
#include <iostream>
#include <thread>
#include <chrono>
#include <mutex>
using namespace std::literals;
std::mutex m;
int share_data = 0;
void foo()
{
if(m.try_lock())
{
share_data = 100;
stds::cout << "using shared_data" << std::endl;
m.unlock();
}
else
{
stds::cout << "mutex획득실패" << std::endl;
}
}
int main()
{
std::thread t1(foo);
std::thread t2(foo);
t1.join();
t2.join();
// OS에서 사용중인 mutex 핸들을 리턴
std::mutex::native_handle_type h = m.native_handle();
std::mutex m2 = m; // Error - 복사생성이 안됨, Move도 안됨
}
mutex Vs timed_mutex
- timed_mutex :
try_lock_for
,try_lock_until
을 지원한다
#include <iostream>
#include <thread>
#include <chrono>
#include <mutex>
using namespace std::literals;
std::timed_mutex m;
int share_data = 0;
void foo()
{
if(m.try_lock_for(2s))
{
share_data = 100;
stds::cout << "using shared_data" << std::endl;
m.unlock();
}
else
{
stds::cout << "mutex획득실패" << std::endl;
}
}
int main()
{
std::thread t1(foo);
std::thread t2(foo);
t1.join();
t2.join();
}
recursive_mutex
// ...
std::mutex m;
int share_data = 0;
void foo()
{
m.lock();
m.lock(); // Error - 다시 lock할순 없다 -> recursive_mutex이용
// ...
#include <iostream>
#include <thread>
#include <chrono>
#include <mutex>
using namespace std::literals;
std::recursive_mutex m;
int share_data = 0;
void foo()
{
m.lock();
m.lock(); // 내부적으로 소유횟수를 관리함을 기억
share_data = 100;
stds::cout << "using shared_data" << std::endl;
m.unlock(); // 다른 thread가 접근하지못함, 한 번더 unlock해야한다
}
int main()
{
std::thread t1(foo);
std::thread t2(foo);
t1.join();
t2.join();
}
Example
class Machine
{
int shared_data = 0;
std::recursive_mutex m;
public:
void f1()
{
m.lock();
shared_data = 100;
m.unlock();
}
void f2()
{
m.lock();
shared_data = 200;
f1();
m.unlock();
}
};
shared_mutex
#include <iostream>
#include <thread>
#include <chrono>
#include <mutex>
#include <string_view>
using namespace std::literals;
std::mutex m;
int share_data = 0;
void Writer()
{
while(1)
{
m.lock();
share_data = share_data + 1;
std::cout << "Writer : " << share_data << std::endl;
std::this_thread::sleep_for(1s);
m.unlock();
std::this_thread::sleep_for(10ms);
}
}
void Reader(std::string_view name)
{
while(1)
{
m.lock();
std::cout << "Reader(" << name << ") : " << share_data << std::endl;
std::this_thread::sleep_for(500ms);
m.unlock();
std::this_thread::sleep_for(10ms);
}
}
int main()
{
std::thread t1(Writer);
std::thread t2(Reader, "A");
std::thread t3(Reader, "B");
std::thread t4(Reader, "C");
t1.join();
t2.join();
t3.join();
t4.join();
// 여기서 생각해 봐야할 문제는 쓰는동안에 읽는건 문제가 될 수 있지만
// 다른 스레드에서 읽는동안에 같이 읽는건 문제될게 없지 않나??
// 개선해보자
}
#include <iostream>
#include <thread>
#include <chrono>
#include <mutex>
#include <string_view>
using namespace std::literals;
std::shared_mutex m;
int share_data = 0;
void Writer()
{
while(1)
{
m.lock();
share_data = share_data + 1;
std::cout << "Writer : " << share_data << std::endl;
std::this_thread::sleep_for(1s);
m.unlock();
std::this_thread::sleep_for(10ms);
}
}
void Reader(std::string_view name)
{
while(1)
{
m.lock_shared();
std::cout << "Reader(" << name << ") : " << share_data << std::endl;
std::this_thread::sleep_for(500ms);
m.unlock_shared();
std::this_thread::sleep_for(10ms);
}
}
int main()
{
std::thread t1(Writer);
std::thread t2(Reader, "A");
std::thread t3(Reader, "B");
std::thread t4(Reader, "C");
t1.join();
t2.join();
t3.join();
t4.join();
}