C++ Concurrency in Action 9-2 Interrupting threads
1. C++ Concurrency in Action Study
C++ Korea
Chapter 09 Advanced thread management
9.2 Interrupting threads
C++ Korea
C++ Concurrency in Action
Study C++ Korea 박 동하 (luncliff@gmail.com)
C++ Korea 윤석준 (seokjoon.yun@gmail.com)
2. C++ Concurrency in Action Study
C++ Korea
Interrupting Thread
• long-running thread를 정지시킬 때
• 동작중인 thread가 아닌 다른 thread에서 작업을 중지시키고 싶을 때
• 특히 사용자가 명시적으로 [작업 취소]를 누른 경우
• GUI (특히 MFC)에서는 Signal (Message)로 처리
• C++11에서는 interrupting thread를 제공해주지 않지만, 쉽게 구현이 가능함
• 기존 thread에 interrupt거는 함수와 interrupt되는 함수 구현이면 끝!
interrupt( ) interrupt_point( )
2
3. C++ Concurrency in Action Study
C++ Korea
Basic Implementation
of interruptible_thread
Simple하게 while-loop 안에서interrupt check
4. C++ Concurrency in Action Study
C++ 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 거는 기능
생성자 : 함수 실행
5. C++ Concurrency in Action Study
C++ 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
6. C++ Concurrency in Action Study
C++ Korea6
Basic Implementation
of interruptible_thread
DEMO !!!
7. C++ Concurrency in Action Study
C++ Korea
DEMO 결과는 ???
7
• 잘 동작함
• 별 문제 없어 보임
• 근데 왜 ??? 작가는 ???
• while-loop에서 interrupt를blocking으로 기다리는 게마음에안 드는듯;;;
• 그래서 ?
• std::condition_variable을 이용하여 interruptible_wait()를 구현
• 추가로 std::mutex와 std::lock_guard<>도 같이 사용을… ;;;
• 하지만program 구조상while-loop를 사용하는 경우는이예제가최고의 solution이지 않을까라는 의견을 살포시조심스럽고 소심하게 제시해봄
8. C++ Concurrency in Action Study
C++ Korea
A Broken version of
interruptible_wait for
std::condition_variable
std::condition_variable::wait()로 대기
but, 깨우는데 쌩까고계속잘 수있음 ;;;
9. C++ Concurrency in Action Study
C++ Korea
interruptible_wait (추가)
9
if set then
throw exception
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();
}
throw exception
10. C++ Concurrency in Action Study
C++ 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() 함수 추가
11. C++ Concurrency in Action Study
C++ Korea
A Broken version of
interruptible_wait for
std::condition_variable
DEMO !!!
12. C++ Concurrency in Action Study
C++ 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가 발생한 경우 ???
13. C++ Concurrency in Action Study
C++ Korea
A Broken version of
interruptible_wait for
std::condition_variable
DEMO !!! #2
14. C++ Concurrency in Action Study
C++ Korea
DEMO 결과는 ???
14
• interrupt 씹혔음 ;;;;
• 다시 보내니깐 정상적으로 interrupt 됨
• 그럼 대안은 ????
• 그 사이를 다른 std::mutex로 보호 ?
• 서로의 수명을 모르는 thread 들끼리 mutex 참조를 통과 -> 위험함
• wait() 대신 wait_for()를 사용하여 대기 시간의 제한을 둠
• spurious wake (가짜 깸)이 자주 일어나겠지만 해결 됨
15. C++ Concurrency in Action Study
C++ Korea
Using timeout in
interruptible_wait for
std::condition_variable
깨우는거쌩까지는 않는데…
while-loop 안에서wait_for( )로 check
16. C++ Concurrency in Action Study
C++ 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();
}
};
17. C++ Concurrency in Action Study
C++ 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();
}
when cv set and
throw exception
without cv clear,
then
cv clear automatically
18. C++ Concurrency in Action Study
C++ 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();
}
19. C++ Concurrency in Action Study
C++ Korea
Using timeout in
interruptible_wait for
std::condition_variable
DEMO !!!
20. C++ Concurrency in Action Study
C++ Korea
DEMO 결과는 ???
20
• 잘 동작함
• 무슨 수를 써도 무조건 잘 동작함
• 근데 std::condition_variable 말고
std::condition_variable_any를쓸려면 ???
(그냥std::unique_lock<std::mutex>만 쓰면될껄…. 또뭘더쓸려고… 참…)
21. C++ Concurrency in Action Study
C++ Korea
interruptible_wait for
std::condition_variable_any
std::condition_variable_any를 작가님이굳이쓰시겠다는데 머…
다시while-loop 없이 wait( )로 대기
22. C++ Concurrency in Action Study
C++ 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() 추가
24. C++ Concurrency in Action Study
C++ Korea
interruptible_wait for
std::condition_variable_any
DEMO !!!
25. C++ Concurrency in Action Study
C++ Korea
DEMO 결과는 ???
25
• 잘 동작함
• 뚤어보려 애썼는데… 안뚤림 ;;;
• 작가님이 한가지만 더 살펴보자는데…
std::future 같이 다른 blocking call에 interrupt를 주려면 ???
26. C++ Concurrency in Action Study
C++ Korea
interrupting
other blocking calls
27. C++ Concurrency in Action Study
C++ 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
28. C++ Concurrency in Action Study
C++ Korea
interrupting
other blocking calls
DEMO ???
Pass !!!
고마보자. 많이봤다. 아이가;;;
29. C++ Concurrency in Action Study
C++ Korea
Handling interrupt
그동안 DEMO에서많이봤는데…
맹그거임.
30. C++ Concurrency in Action Study
C++ Korea
Handling interrupt
30
딱히 설명이 필요할지…
try
{
do_something();
}
catch (thread_interrupted&)
{
handle_interrupt();
}
31. C++ Concurrency in Action Study
C++ Korea
Handling interrupt
31
딱히 설명이 필요할지…
try
{
do_something();
}
catch (thread_interrupted&)
{
handle_interrupt();
}
- 그런데, 이렇게 외부에서 exception을 처리 하는 경우, 만약 실수로 처리를 안하게 되면 ?
std::terminate()가 호출되어 program이 종료 될 수도 있다.
- 왠만하면 thread 내부에서 exception을 처리해 주는게 안전하다.
32. C++ Concurrency in Action Study
C++ 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();
}
33. C++ Concurrency in Action Study
C++ Korea
Summary
33
• 여러가지 방법으로 thread를 interrupt하는 방법을 살펴 봄.
• while-loop 안에서 interruption_point( ) 체크
• std::condition_variable, std::condition_variable_any를 이용
• 다른 blocking 방법에 대한 예제 (std::future)