c++ concurrency in action 9-2 interrupting threads
TRANSCRIPT
C++ Concurrency in Action StudyC++ Korea
Chapter09 Advancedthreadmanagement9.2Interruptingthreads
C++ KoreaC++ Concurrency in Action Study C++ Korea 박 동하 ([email protected])
C++ Korea 윤석준 ([email protected])
C++ Concurrency in Action StudyC++ Korea
Interrupting Thread
• long-runningthread를 정지시킬 때
• 동작중인 thread가 아닌다른 thread에서 작업을중지시키고 싶을 때
• 특히사용자가 명시적으로 [작업 취소]를 누른경우
• GUI(특히MFC)에서는 Signal(Message)로 처리
• C++11에서는 interruptingthread를 제공해주지 않지만,쉽게구현이 가능함
• 기존 thread에 interrupt거는 함수와 interrupt되는 함수구현이면 끝!interrupt( ) interrupt_point( )
2
C++ Concurrency in Action StudyC++ Korea
Basic Implementationof interruptible_thread
Simple하게while-loop안에서 interruptcheck
C++ Concurrency in Action StudyC++ Korea
interruptible_thread
4
class interruptible_thread{
std::thread internal_thread;interrupt_flag* flag;
public:template<typename FunctionType>interruptible_thread(FunctionType f){
std::promise<interrupt_flag*> p;internal_thread = std::thread([f, &p] {
p.set_value(&this_thread_interrupt_flag);f();
});flag = p.get_future().get();
}
void interrupt(){
if (flag)flag->set();
}
void join() { internal_thread.join(); }void detach() { internal_thread.detach(); }bool joinable() const { return internal_thread.joinable(); }
};
std::thread기능
interrupt거는 기능
생성자 :함수 실행
C++ Concurrency in Action StudyC++ Korea
interrupt_flag
5
class interrupt_flag{
std::atomic<bool> flag;public:
void set(){
flag.store(true, std::memory_order_relaxed);}
bool is_set() const{
return flag.load(std::memory_order_relaxed);}
};
thread_local interrupt_flag this_thread_interrupt_flag;
void interruption_point(){
if (this_thread_interrupt_flag.is_set())throw thread_interrupted();
}
interrupt거는 기능
각 thread별로 flag를 가짐
interrupt flag
C++ Concurrency in Action StudyC++ Korea
DEMO 결과는 ???
7
• 잘 동작함
• 별 문제 없어 보임
• 근데 왜 ???작가는 ???• while-loop에서 interrupt를blocking으로 기다리는 게마음에안 드는듯;;;
• 그래서 ?• std::condition_variable을 이용하여 interruptible_wait()를 구현
• 추가로 std::mutex와 std::lock_guard<>도 같이 사용을… ;;;
• 하지만program구조상while-loop를 사용하는 경우는이예제가최고의 solution이지 않을까라는 의견을 살포시조심스럽고 소심하게 제시해봄
C++ Concurrency in Action StudyC++ Korea
A Broken version of interruptible_wait for std::condition_variable
std::condition_variable::wait()로 대기but,깨우는데 쌩까고계속잘 수있음 ;;;
C++ Concurrency in Action StudyC++ Korea
interruptible_wait (추가)
9
ifsetthenthrowexception
void interruptible_wait(std::condition_variable& cv,std::unique_lock<std::mutex>& lk)
{interruption_point();this_thread_interrupt_flag.set_condition_variable(cv);cv.wait(lk);this_thread_interrupt_flag.clear_condition_variable();interruption_point();
}throwexception
C++ Concurrency in Action StudyC++ Korea
interrupt_flag (수정)
std::condition_variable*추가
보호할 목적의 std::mutex 추가
class interrupt_flag{std::atomic<bool> flag;std::condition_variable* thread_cond;std::mutex set_clear_mutex;
public:interrupt_flag() : thread_cond(0) {}
void set(){flag.store(true, std::memory_order_relaxed);std::lock_guard<std::mutex> lk(set_clear_mutex);if (thread_cond)thread_cond->notify_all();
}bool is_set() const{return flag.load(std::memory_order_relaxed);
}void set_condition_variable(std::condition_variable& cv){std::lock_guard<std::mutex> lk(set_clear_mutex);thread_cond = &cv;
}void clear_condition_variable(){std::lock_guard<std::mutex> lk(set_clear_mutex);thread_cond = 0;
}};
set()에 std::condition_variable::notify_all( )추가
std::condition_variable이 설정되지 않아도 정상동작
std::condition_variable set(),clear()함수 추가
C++ Concurrency in Action StudyC++ Korea
A Broken version of interruptible_wait for std::condition_variableDEMO !!!
C++ Concurrency in Action StudyC++ Korea
DEMO 결과는 ???
12
• 잘 동작함
• 별 문제 없어 보임
• 앞서 본while-loop 형식도 여기서 잘 동작함
• 근데 ????void interruptible_wait(std::condition_variable& cv, std::unique_lock<std::mutex>& lk){
interruption_point();this_thread_interrupt_flag.set_condition_variable(cv);cv.wait(lk);this_thread_interrupt_flag.clear_condition_variable();interruption_point();
}
이 사이에 interrupt가 발생한 경우 ???
C++ Concurrency in Action StudyC++ Korea
A Broken version of interruptible_wait for std::condition_variableDEMO !!! #2
C++ Concurrency in Action StudyC++ Korea
DEMO 결과는 ???
14
• interrupt씹혔음 ;;;;
• 다시 보내니깐 정상적으로 interrupt됨
• 그럼 대안은 ????• 그 사이를 다른 std::mutex로 보호 ?
• 서로의 수명을 모르는 thread들끼리mutex참조를 통과 ->위험함
• wait()대신wait_for()를 사용하여 대기 시간의 제한을 둠
• spuriouswake(가짜 깸)이 자주 일어나겠지만 해결 됨
C++ Concurrency in Action StudyC++ Korea
Using timeout in interruptible_wait for std::condition_variable
깨우는거쌩까지는 않는데…while-loop안에서wait_for()로 check
C++ Concurrency in Action StudyC++ Korea
clear_cv_on_destruct (추가)
16
std::condition_variable의clear()를 RAII로 관리
struct clear_cv_on_destruct{
~clear_cv_on_destruct(){
this_thread_interrupt_flag.clear_condition_variable();}
};
C++ Concurrency in Action StudyC++ Korea
interruptible_wait (수정)
17
void interruptible_wait(std::condition_variable& cv,std::unique_lock<std::mutex>& lk)
{interruption_point();this_thread_interrupt_flag.set_condition_variable(cv);clear_cv_on_destruct guard;interruption_point();cv.wait_for(lk, std::chrono::milliseconds(1));interruption_point();
}
whencvsetandthrowexceptionwithoutcvclear,
thencvclearautomatically
C++ Concurrency in Action StudyC++ Korea
interruptible_wait using predicate (추가)
18
template<typename Predicate>void interruptible_wait(std::condition_variable& cv,
std::unique_lock<std::mutex>& lk,Predicate pred)
{interruption_point();this_thread_interrupt_flag.set_condition_variable(cv);interrupt_flag::clear_cv_on_destruct guard;while (!thie_thread_interrupt_flag.is_set() && !pred()){
cv.wait_for(lk, std::chrono::milliseconds(1));}interruption_point();
}
C++ Concurrency in Action StudyC++ Korea
Using timeout in interruptible_wait for std::condition_variableDEMO !!!
C++ Concurrency in Action StudyC++ Korea
DEMO 결과는 ???
20
• 잘 동작함
• 무슨 수를 써도 무조건 잘 동작함
• 근데 std::condition_variable 말고std::condition_variable_any를쓸려면 ???(그냥std::unique_lock<std::mutex>만 쓰면될껄….또뭘더쓸려고…참…)
C++ Concurrency in Action StudyC++ Korea
interruptible_wait for std::condition_variable_any
std::condition_variable_any를 작가님이굳이쓰시겠다는데 머…다시while-loop없이wait()로 대기
C++ Concurrency in Action StudyC++ Korea
interrupt_flag에 std::condition_variable_any 추가
22
class interrupt_flag{
std::atomic<bool> flag;std::condition_variable* thread_cond;std::condition_variable_any* thread_cond_any;std::mutex set_clear_mutex;
public:interrupt_flag(): thread_cond(0), thread_cond_any(0) {}
void set(){flag.store(true, std::memory_order_relaxed);std::lock_guard<std::mutex> lk(set_clear_mutex);if (thread_cond)thread_cond->notify_all();
else if (thread_cond_any)thread_cond_any->notify_all();
}
std::condition_variable_any*추가
std::condition_variable_any의 notify_all()추가
C++ Concurrency in Action StudyC++ Korea
interrupt_flag에 wait( ) (추가)
23
template<typename Lockable>void interrupt_flag::wait(std::condition_variable_any& cv,
Lockable& lk){
struct custom_lock { … };
custom_lock cl(this, cv, lk);interruption_point();cv.wait(cl);interruption_point();
}
struct custom_lock{interrupt_flag* self;Lockable& lk;custom_lock(interrupt_flag* self_,
std::condition_variable_any& cond,Lockable& lk_)
: self(self_), lk(lk_)
{self->set_clear_mutex.lock();self->thread_cond_any = &cond;
}void unlock(){lk.unlock();self->set_clear_mutex.unlock();
}void lock(){std::lock(self->set_clear_mutex, lk);
}~custom_lock(){self->thread_cond_any = 0;self->set_clear_mutex.unlock();
}};
lock
unlock
exception이 발생하여도 무조건 unlock
template<typename Lockable>void interruptible_wait(std::condition_variable_any& cv,
Lockable& lk){
this_thread_interrupt_flag.wait(cv, lk);}
C++ Concurrency in Action StudyC++ Korea
DEMO 결과는 ???
25
• 잘 동작함
• 뚤어보려 애썼는데…안뚤림 ;;;
• 작가님이 한가지만 더살펴보자는데…std::future 같이 다른 blockingcall에 interrupt를 주려면 ???
C++ Concurrency in Action StudyC++ Korea
interruptible_wait( ) using std::future
27
interrupt가 걸려도 빠져나가고
template<typename T>void interruptible_wait(std::future<T>& uf){
while (!this_thread_interrupt_flag.is_set()){
if (uf.wait_for(lk, std::future_status::ready== std::chrono::milliseconds(1)))
break;}
}
작업이 끝나도 빠져나가고
주기적으로 check
C++ Concurrency in Action StudyC++ Korea
interruptingother blocking callsDEMO ???
Pass !!!고마보자.많이봤다.아이가 ;;;
C++ Concurrency in Action StudyC++ Korea
Handling interrupt
30
딱히 설명이 필요할지…
try{
do_something();}catch (thread_interrupted&){
handle_interrupt();}
C++ Concurrency in Action StudyC++ Korea
Handling interrupt
31
딱히 설명이 필요할지…
try{
do_something();}catch (thread_interrupted&){
handle_interrupt();}
- 그런데,이렇게 외부에서 exception을 처리 하는 경우,만약 실수로 처리를 안하게 되면 ?
std::terminate()가 호출되어 program이 종료 될 수도 있다.
- 왠만하면 thread내부에서 exception을 처리해 주는게 안전하다.
C++ Concurrency in Action StudyC++ Korea
Handling interrupt in internal_thread constructor
32
아에 internal_thread 생성시
함수 실행 부분에서 exception 처리를했다.
template<typename FunctionType>interruptible_thread(FunctionType f){
std::promise<interrupt_flag*> p;internal_thread = std::thread([f, &p] {
p.set_value(&this_thread_interrupt_flag);try{
f();}catch (thread_interrupted const&){
handle_interrupt();}
});flag = p.get_future().get();
}
C++ Concurrency in Action StudyC++ Korea
Summary
33
• 여러가지방법으로 thread를 interrupt하는 방법을 살펴 봄.
• while-loop안에서 interruption_point( )체크
• std::condition_variable, std::condition_variable_any를 이용
• 다른 blocking방법에 대한 예제 (std::future)