c++ concurrency in action 9-2 interrupting threads

34
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 박 동하 ([email protected]) C++ Korea 윤석준 ( [email protected])

Upload: seok-joon-yun

Post on 21-Mar-2017

510 views

Category:

Software


4 download

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++ Korea6

Basic Implementationof interruptible_threadDEMO !!!

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

interruptible_wait for std::condition_variable_anyDEMO !!!

C++ Concurrency in Action StudyC++ Korea

DEMO 결과는 ???

25

• 잘 동작함

• 뚤어보려 애썼는데…안뚤림 ;;;

• 작가님이 한가지만 더살펴보자는데…std::future 같이 다른 blockingcall에 interrupt를 주려면 ???

C++ Concurrency in Action StudyC++ Korea

interruptingother blocking calls

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

그동안DEMO에서많이봤는데…맹그거임.

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)

C++ Concurrency in Action StudyC++ Korea

Q & A