SlideShare a Scribd company logo
1 of 177
Download to read offline
ˠ˱́˱˼˼˶˼̍˾̌˶˳̌̈˹̂˼˹̃˶˼̍˾̌˶̃˶̆˾˿˼˿˴˹˹ 
˟̂˶˾̍3DUDOOHORPSXWLQJ7HFKQRORJLHV37
˜˶˻̇˹̐˝˾˿˴˿̀˿̃˿̈˾˿˶ 
̀́˿˴́˱˽˽˹́˿˳˱˾˹˶˳̐˸̌˻˶ˢ 
ˡ˱˲˿̃˱̂̀˿̃˿˻˱˽˹˘˱̊˹̃˱˵˱˾˾̌̆ 
ˢ˹˾̆́˿˾˹˸˱̇˹̐˒̄˵̄̊˹˶́˶˸̄˼̍̃˱̃̌ 
ˠ˱˸˾˹˻˿˳ˑ˼˶˻̂˶˺ˑ˼˶˻̂˱˾˵́˿˳˹̈ 
˛˱̅˶˵́˱˳̌̈˹̂˼˹̃˶˼̍˾̌̆̂˹̂̃˶˽ˢ˹˲˔ˤˣ˙ 
ˢ˱˺̃˻̄́̂˱KWWSFSFWVLEVXWLVUXaDSD]QLNRYWHDFKLQJ 
˓˿̀́˿̂̌KWWSVSLD]]DFRPVLEVXWLVUXIDOOSFWKRPH
ˢ˿˸˵˱˾˹˶˹˸˱˳˶́̉˶˾˹˶ 
́˱˲˿̃̌̀˿̃˿˻˿˳ 
2
˟˵˹˳˾̌˺˾˿˳̌˺̀˱́˱˼˼˶˼̍˾̌˺@˽˹́ 
#include iostream 
#include thread 
void hello() { // функция, которая реализует поток 
std::cout  hello brave new world!n; 
} 
int main() { 
std::thread t(hello); // создаём поток 
t.join(); // дожидаемся завершения 
} 
3
˘˱̀̄̂˻̀˿̃˿˻˱ 
#include iostream 
#include thread 
void hello() { 
class thread_class { // класс с перегруженным оператором() 
public: 
void operator()() const { 
hello(); 
bye(); 
} 
} 
std::cout  hello brave new world!n; 
} 
int main() { 
std::thread t(hello); 
t.join(); 
} 
4
˘˱̀̄̂˻̀˿̃˿˻˱ 
class thread_class { 
public: 
void operator()() const { 
hello(); 
bye(); 
} 
} 
std::thread thr((thread_class())); // зачем ()? 
std::thread thr{thread_class} // так лучше! 
std::thread thr([](){ 
std::cout  hello worldn; 
}); 
thr.join(); 
Most vexing parse 
5
˟̃̂˿˶˵˹˾̒˾˾̌˺̀˿̃˿˻ 
struct func { 
int i; 
func(int _i): i{_i} {} 
void operator()() { 
std::cout  i  std::endl;// доступ к висячей сслыке 
} 
}; 
int main(int argc, const char *argv[]) 
{ 
int local = 100; 
func myfunc(local); 
std::thread thr(myfunc); 
thr.detach(); // отсоединяем поток... 
} // поток ещё работает! 
6
˟˷˹˵˱˾˹˶˸˱˳˶́̉˶˾˹̐̀˿̃˿˻˱˳̂˼̄̈˱˶˹̂˻˼̏̈˶˾˹̐ 
std::thread thr(myfunc); 
try { 
std::cout  hello; 
// ... 
throw error; 
} 
catch (...) { 
thr.join(); // не забыть дождаться завершения 
std::cout  exception catchedn; 
return 1; 
} 
thr.join(); 
7
˟˷˹˵˱˾˹˶˸˱˳˶́̉˶˾˹̐̀˿̃˿˻˱˳̂˼̄̈˱˶˹̂˻˼̏̈˶˾˹̐ 
class thread_guard { 
std::thread t; 
public: 
explicit thread_guard(std::thread _t): t{_t} {} 
~thread_guard() { 
if (t.joinable()) { 
t.join(); 
} 
} 
thread_guard(thread_guard const) = delete; 
thread_guard operator=(thread_guard const) = delete; 
}; 
void foo() { 
int local; 
std::thread t{func(local)}; 
thread_guard g(t); 
} // t.join() 
8
˘˱̀̄̂˻˾˶̂˻˿˼̍˻˹̆̀˿̃˿˻˿˳˹˿˷˹˵˱˾˹˶˸˱˳˶́̉˶˾˹̐ 
int main() { 
std::vectorstd::thread threads; 
for (auto i = 0; i  10; i++) { 
threads.push_back(std::thread([i](){ 
std::cout  i  n; })); 
} 
for_each(threads.begin(), threads.end(), 
std::mem_fn(std::thread::join)); 
} 
9
˘˱̀̄̂˻˾˶̂˻˿˼̍˻˹̆̀˿̃˿˻˿˳˹˹˵˶˾̃˹̅˹˻˱̃˿́̌̀˿̃˿˻˿˳ 
std::vectorstd::thread threads; 
std::mapstd::thread::id, int table; 
for (auto i = 0; i  10; i++) { 
threads.push_back(std::thread([i](){ 
std::this_thread::sleep_for( 
std::chrono::milliseconds(100 * i)); 
std::cout  i  n; 
})); 
table.insert(std::make_pair(threads.back().get_id(), 
i % 2)); 
} 
std::cout  value of 5:   table[threads[5].get_id()] 
 std::endl; 
std::cout  value of 6:   table[threads[6].get_id()] 
 std::endl; 
for_each(threads.begin(), threads.end(), 
std::mem_fn(std::thread::join)); 10
ˠ˶́˶˵˱̈˱˱́˴̄˽˶˾̃˿˳̅̄˾˻̇˹˹̀˿̃˿˻˱ 
void func(int i, std::string const s1, 
std::string const s2) { 
std::cout  s1     s2  std::endl; 
} 
int main() { 
std::thread t(func, 2014, hello, world); 
t.join(); 
} 
11
ˠ˶́˶˵˱̈˱˱́˴̄˽˶˾̃˿˳̅̄˾˻̇˹˹̀˿̃˿˻˱ 
void func(int i, std::string const s1, 
std::string const s2) { 
std::cout  s1     s2  std::endl; 
} 
int main() { 
char buf[] = hello; 
std::thread t(func, 2014, buf, world); 
t.detach(); 
} 
12
ˠ˶́˶˵˱̈˱˱́˴̄˽˶˾̃˿˳̅̄˾˻̇˹˹̀˿̃˿˻˱ 
void func(int i, std::string const s1, 
std::string const s2) { 
std::cout  s1     s2  std::endl; 
} 
int main() { 
char buf[] = hello; // автоматическая переменная 
std::thread t(func, 2014, buf, world); 
t.detach(); 
} // переменной foo нет, 
// а поток продолжает выполняться 
Использование 
висячего указателя 
13
ˠ˶́˶˵˱̈˱˱́˴̄˽˶˾̃˿˳̅̄˾˻̇˹˹̀˿̃˿˻˱ 
void func(int i, std::string const s1, 
std::string const s2) { 
std::cout  s1     s2  std::endl; 
} 
int main() { 
char buf[] = hello; 
std::thread t(func, 2014, std::string(buf), world); 
t.detach(); 
} 
Явное преобразование 
позволяет избежать 
висячего указателя 
14
ˠ˶́˶˵˱̈˱˱́˴̄˽˶˾̃˿˳̅̄˾˻̇˹˹̀˿̃˿˻˱ 
void func(std::string s_arg) { 
s_arg = hello parallel world; 
} 
int main() { 
std::string s{hello world}; 
std::thread t(func, s); 
t.join(); 
std::cout  s  std::endl; // результат операции? 
return 0; 
} 
15
ˠ˶́˶˵˱̈˱˱́˴̄˽˶˾̃˿˳̅̄˾˻̇˹˹̀˿̃˿˻˱̀˿̂̂̌˼˻˶ 
void func(std::string s_arg) { 
s_arg = hello parallel world; 
} 
int main() { 
std::string s{hello world}; 
std::thread t(func, std::ref(s)); // передача по ссылке! 
t.join(); 
std::cout  s  std::endl; // hello parallel world 
return 0; 
} 
16
ˠ˶́˶˵˱̈˱̄̀́˱˳˼˶˾˹̐̀˿̃˿˻˿˽ 
struct bulky { // некий массивный объект 
std::string name; 
void print() 
{ std::cout  I'm   name  std::endl; } 
}; 
void func(std::unique_ptrbulky obj) { 
obj-print(); 
} 
int main() { 
std::unique_ptrbulky ptr{new bulky{Ivan}}; 
std::thread t(func, ptr); 
t.join(); 
} 
17
ˠ˶́˶˵˱̈˱˱́˴̄˽˶˾̃˿˳ 
struct bulky { // некий массивный объект 
std::string name; 
void print() 
{ std::cout  I'm   name  std::endl; } 
}; 
void func(std::unique_ptrbulky obj) { 
obj-print(); 
} 
int main() { 
std::unique_ptrbulky ptr{new bulky{Ivan}}; 
std::thread t(func, std::move(ptr)); 
t.join(); 
} 
18
ˠ˶́˶˵˱̈˱̄̀́˱˳˼˶˾˹̐̀˿̃˿˻˿˽ 
void foo() { } 
void bar() { } 
int main() { 
std::thread t1(foo); 
std::thread t2 = std::move(t1); // перемещение 
t1 = std::thread(bar); 
std::thread t3; 
t1 = std::move(t3); // ошибка! 
t3 = std::move(t2); 
t3 = std::move(t2); // ошибка! 
t1.join(); 
t2.join(); // ошибка! 
t3.join(); 
} 
19
ˠ˶́˶˵˱̈˱̄̀́˱˳˼˶˾˹̐̀˿̃˿˻˿˽ 
std::thread foo() { 
std::thread thr([](){ std::cout  threadn; }); 
return thr; // перемещение 
} 
void bar(std::thread thr) { thr.join(); } 
int main() { 
std::thread thr = foo(); 
bar(std::move(thr)); // перемещение 
return 0; 
} 
20
ˠ˶́˶˵˱̈˱̄̀́˱˳˼˶˾˹̐̀˿̃˿˻˿˽ 
class scoped_thread { 
std::thread t; 
public: 
explicit scoped_thread(std::thread _t): t{std::move(_t)} 
{ if (!t.joinable()) 
throw std::logic_error(no thread); } 
~scoped_thread() { 
t.join(); 
} 
scoped_thread(thread_guard const) = delete; 
scoped_thread operator=(thread_guard const) = delete; 
}; 
void foo() { 
int local; 
scoped_thread t{std::thread(func((local))}; 
} 
21
ˠ˱́˱˼˼˶˼̍˾˱̐˳˶́̂˹̐˱˼˴˿́˹̃˽˱DFFXPXODWH 
38 38 38 
accumulate_block accumulate_block accumulate_block 
Поток 1 Поток 2 Поток 
num_threads 
22
ˠ˱́˱˼˼˶˼̍˾˱̐˳˶́̂˹̐˱˼˴˿́˹̃˽˱DFFXPXODWH 
const int SIZE = 10; 
templatetypename Iterator, typename T, 
typename BinOperation 
struct accumulate_block { 
// каждый поток рассчитывает свой блок 
void operator()(Iterator first, Iterator last, 
T result, BinOperation op) { 
result = std::accumulate(first, last, result, op); 
} 
}; 
23
ˠ˱́˱˼˼˶˼̍˾˱̐˳˶́̂˹̐˱˼˴˿́˹̃˽˱DFFXPXODWH 
templatetypename Iterator, typename T, typename BinOp 
T parallel_accumulate(Iterator first, Iterator last, 
T init, BinOp op) { 
unsigned long const length = std::distance(first, last); 
if (!length) return init; 
макс. число 
unsigned long const min_per_thread = 2; 
потоков 
unsigned long const max_threads = 
(length + min_per_thread - 1) / min_per_thread; 
unsigned long const hardware_threads = 
std::thread::hardware_concurrency(); 
unsigned long const num_threads = 
аппаратный 
предел числа 
потоков (ядер) 
std::min(hardware_threads != 0 ? hardware_threads : 2, 
max_threads); 
число потоков размер блока 
unsigned long const block_size = length / num_threads; 
std::vectorT results(num_threads); 
std::vectorstd::thread threads(num_threads - 1); 
24
ˠ˱́˱˼˼˶˼̍˾˱̐˳˶́̂˹̐˱˼˴˿́˹̃˽˱DFFXPXODWH 
Iterator block_start = first; 
for (unsigned long i = 0; i  num_threads - 1; i++) { 
Iterator block_end = block_start; 
std::advance(block_end, block_size); // конец блока 
// каждый поток рассчитывает свой блок 
threads[i] = std::thread( 
accumulate_blockIterator, T, BinOperation(), 
block_start, block_end, std::ref(results[i]), op); 
block_start = block_end; 
последний блок 
(+остаток) 
} 
accumulate_blockIterator, T, BinOperation() 
(block_start, last, results[num_threads - 1], op); 
std::for_each(threads.begin(), threads.end(), 
std::mem_fn(std::thread::join)); 
return std::accumulate(results.begin(), results.end(), 
init, op); } 
25
ˠ˱́˱˼˼˶˼̍˾˱̐˳˶́̂˹̐˱˼˴˿́˹̃˽˱DFFXPXODWH 
int main() { 
std::vectorint vec(SIZE); 
for (auto x: vec) 
x = rand() % 10; 
std::cout  acc:  
 parallel_accumulate(vec.begin(), vec.end(), 0, 
std::plusint())  std::endl; 
return 0; 
} 
26
˘˱̊˹̃˱́˱˸˵˶˼̐˶˽̌̆ 
˵˱˾˾̌̆˽˶˷˵̄ 
̀˿̃˿˻˱˽˹ 
˹̂˹˾̆́˿˾˹˸˱̇˹̐ 
27
˝̍̏̃˶˻̂̌˳ˢ 
std::listint mylist; 
std::mutex lock; 
void add(int elem) { 
std::lock_guardstd::mutex guard(lock); 
mylist.push_back(elem); 
} 
bool find(int elem) { 
std::lock_guardstd::mutex guard(lock); 
return std::find(mylist.begin(), mylist.end(), elem) 
!= mylist.end(); 
} 
28
˝̍̏̃˶˻̂̌˳ˢ 
std::listint mylist; 
std::mutex lock; 
void add(int elem) { 
std::lock_guardstd::mutex guard(lock); 
if (elem  0) 
throw error; 
mylist.push_back(elem); 
} 
bool find(int elem) { 
std::lock_guardstd::mutex guard(lock); 
if (mylist.size() == 0) 
return false; 
return std::find(mylist.begin(), mylist.end(), elem) 
!= mylist.end(); 
} 
29
˝̍̏̃˶˻̂̌˳ˢ 
class wrapper { 
private: 
data_t data; // защищаемые данные 
std::mutex mut; 
public: 
templatetypename Function 
void proc_data(Function func) { 
std::lock_guardstd::mutex lock(mut); 
func(data); } 
}; 
data_t *unprotected; // внешний указатель 
void unsafe_func(data_t protected) { 
unprotected = protected; 
Любой код, 
имеющий 
доступ к 
указателю или 
ссылке, может 
делать с ним 
всё, что угодно, 
не захватывая 
мьютекс. 
} 
wrapper obj; 
obj.proc_data(unsafe_func); 
unprotected-do_something(); // незащищённый доступ к data 
30
˝̍̏̃˶˻̂̌˳ˢ 
class wrapper { 
private: 
data_t data; // защищаемые данные 
std::mutex mut; 
public: 
templatetypename Function 
void proc_data(Function func) { 
Нельзя передавать указатели и 
ссылки на защищённые данные 
за пределы области видимости 
блокировки никаким образом. 
std::lock_guardstd::mutex lock(mut); 
func(data); } 
}; 
data_t *unprotected; // внешний указатель 
void unsafe_func(data_t protected) { 
unprotected = protected; 
Любой код, 
имеющий 
доступ к 
указателю или 
ссылке, может 
делать с ним 
всё, что угодно, 
не захватывая 
мьютекс. 
} 
wrapper obj; 
obj.proc_data(unsafe_func); 
unprotected-do_something(); // незащищённый доступ к data 
31
ˑ˵˱̀̃˱̇˹̐˹˾̃˶́̅˶˺̂˿˳˻˽˾˿˴˿̀˿̃˿̈˾˿̂̃˹ 
template ... class stack { 
public: 
// ... 
bool empty() const; 
size_t size() const; 
T top(); 
T const top() const; 
void push(T const); 
void push(T); 
void pop(); 
void swap(stack); 
}; 
32
ˑ˵˱̀̃˱̇˹̐˹˾̃˶́̅˶˺̂˿˳˻˽˾˿˴˿̀˿̃˿̈˾˿̂̃˹ 
template ... class stack { 
public: 
// ... 
bool empty() const; 
size_t size() const; 
T top(); 
T const top() const; 
void push(T const); 
void push(T); 
void pop(); 
void swap(stack); 
}; 
33
ˑ˵˱̀̃˱̇˹̐˹˾̃˶́̅˶˺̂˿˳˻˽˾˿˴˿̀˿̃˿̈˾˿̂̃˹ 
template ... class stack { 
public: 
// ... 
bool empty() const; 
size_t size() const; 
T top(); 
T const top() const; 
void push(T const); 
void push(T); 
void pop(); 
void swap(stack); 
}; 
stackint s; 
if (!s.empty()) { 
int const value = s.top(); 
s.pop(); 
// ... 
} 
некорректный 
результат как решить? 
34
ˑ˵˱̀̃˱̇˹̐˹˾̃˶́̅˶˺̂˿˳˻˽˾˿˴˿̀˿̃˿̈˾˿̂̃˹ 
1. Передавать ссылку в функцию pop 
std::vectorint result; 
mystack.pop(result); 
2. Потребовать наличия копирующего или перемещающего 
конструктора, не возбуждающего исключений (доказано, что 
можно объединить pop и top, но это можно сделать только если 
конструкторы не вызывают исключений) 
3. Возвращать указатель на вытолкнутый элемент 
std::shared_ptrT pop() 
4. Одновременно 1 и один из вариантов 2 или 3 
35
ˠ˿̃˿˻˿˲˶˸˿̀˱̂˾̌˺̂̃˶˻ 
templatetypename T 
class safe_stack { 
private: 
std::stackT data; 
mutable std::mutex m; 
public: 
safe_stack(); 
safe_stack(const safe_stack); 
// стек нельзя присваивать 
safe_stack operator=(const safe_stack) = delete; 
void push(T new_value); 
std::shared_ptrT pop(); 
void pop(T value); 
bool empty() const; 
// swap отсутствует 
// -- интерфейс предельно упрощён -- 
}; 
36
ˠ˿̃˿˻˿˲˶˸˿̀˱̂˾̌˺̂̃˶˻ 
safe_stack(const safe_stack rhs) { 
std::lock_guardstd::mutex lock(rhs.m); 
data = rhs.data; 
} 
std::shared_ptrT pop() { 
std::lock_guardstd::mutex lock(m); 
if (data.empty()) throw empty_stack(); 
// выделяем память под возвращаемое значение 
std::shared_ptrT const 
res(std::make_sharedT(data.top())); 
data.pop(); 
return res; 
} 
void pop(T value) { 
std::lock_guardstd::mutex lock(m); 
if (data.empty()) throw empty_stack(); 
value = data.top(); 
data.pop(); 
} 
37
˕˶˵˼˿˻˹˸˱̆˳˱̃˾˶̂˻˿˼̍˻˹̆˽̍̏̃˶˻̂˿˳ 
class Widget { 
private: 
data obj; 
std::mutex m; 
public: 
Widget(data const d): data(d) {} 
friend void swap(Widget lhs, Widget rhs) { 
if (lhs == rhs) 
return; 
std::lock(lhs.m, rhs.m); // захыватываем мьютекс 
// adopt_lock: lock_a и lock_b начинают владеть 
// захваченной блокировкой 
std::lock_guardstd::mutex 
lock_a(lhs.m, std::adopt_lock); 
std::lock_guardstd::mutex 
lock_b(rhs.m, std::adopt_lock); 
swap(lhs.some_detail, rhs.some_detail); 
} 
}; 
lock - “всё или ничего” 
38
˕˶˵˼˿˻˹˹˶́˱́̆˹̈˶̂˻˹˶˽̍̏̃˶˻̂̌ 
hierarchical_mutex(100) hierarchical_mutex(50) hierarchical_mutex(30) 
1 - 2 - 3 
1 
2 
3 
2 - 3 3 - 1 3 - 2 
порядок запирания 39
˕˶˵˼˿˻˹˹˶́˱́̆˹̈˶̂˻˹˶˽̍̏̃˶˻̂̌ 
1 
2 
3 
hierarchical_mutex(100) hierarchical_mutex(50) hierarchical_mutex(30) 
1 - 2 - 3 2 - 3 3 - 1 3 - 2 
порядок запирания 
40
˕˶˵˼˿˻˹˹˶́˱́̆˹̈˶̂˻˹˶˽̍̏̃˶˻̂̌̀́˹˽˶́ 
hier_mutex high_level_mut(10000); 
hier_mutex low_level_mut(5000); 
int low_level_func() { // низкоуровневая блокировка 
std::lock_guardhier_mutex lock(low_level_mut); 
do_low_level_stuff(); 
} 
void high_level_func() { // высокоуровневая блокировка 
std::lock_guardhier_mut lock(high_level_mut); 
do_high_level_stuff(low_level_func()); // корректный 
} // порядок 
void thread_a() { 
high_level_func(); // всё ок 
} 
hier_mutex lowest_level_mut(100); 
void thread_b() { // некорректный порядок блокировки! 
std::lock_guardhier_mut lock(lowest_level_mut); 
high_level_func(); // вызов недопустим 
} 41
˙˶́˱́̆˹̈˶̂˻˹˶˽̍̏̃˶˻̂̌˳˿˸˽˿˷˾˱̐́˶˱˼˹˸˱̇˹̐ 
class hier_mutex { // hierarchical mutex 
private: 
std::mutex internal_mut; 
unsigned long const hier_val; // текущий уровень 
unsigned prev_hier_val; // предыдущий уровень 
// уровень иерархии текущего потока 
static thread_local unsigned long this_thread_hier_val; 
void check_for_hier_violation() { 
if (this_thread_hier_val = hier_val) { 
throw std::logic_error(mutex hierarchy violated); 
} 
} 
// обновить текущий уровень иерархии потока 
void update_hier_val() { 
prev_hier_val = this_thread_hier_val; 
this_thread_hier_val = hier_val; 
} 
42
˙˶́˱́̆˹̈˶̂˻˹˶˽̍̏̃˶˻̂̌˳˿˸˽˿˷˾˱̐́˶˱˼˹˸˱̇˹̐ 
public: 
explicit hier_mutex(unsigned long value): 
hier_val(value), prev_hier_value(0) {} 
void lock() { 
check_for_hier_violation(); 
internal_mutex.lock(); 
update_hier_val(); 
} 
void unlock() { 
this_thrad_hier_val = prev_hier_val; 
internal_mutex.unlock(); 
} 
void trylock() { 
// ... 
} 
thread_local unsigned long 
hier_mutex::this_thread_hier_val(ULONG_MAX); 
43
˒˼˿˻˹́˿˳˻˱̂̀˿˽˿̊̍̏VWGXQLTXHBORFN 
class Widget { 
int val; 
std::mutex m; 
int getval() const { 
return val; 
} 
}; 
bool Cmp(Widget lhs, Widget rhs) { 
// не захватываем пока мьютексы 
std::unique_lockstd::mutex lock1(lhs.m,std::defer_lock); 
std::unique_lockstd::mutex lock2(rhs.m,std::defer_lock); 
// а вот сейчас захватываем, причём без дедлоков 
std::lock(lock1, lock2); 
return lhs.getval()  rhs.getval() ? true : false; 
} 
44
˒˼˿˻˹́˿˳˻˱̂̀˿˽˿̊̍̏VWGXQLTXHBORFN 
class Widget { 
int val; 
std::mutex m; 
int getval() const { 
std::lock_guardstd::mutex lock(m); 
return val; 
} 
}; 
bool Cmp(Widget lhs, Widget rhs) { 
// обе операции совершаются под защитой мьютекса 
int const lhs_val = lhs.getval(); 
int const rhs_val = rhs.getval(); 
std::lock(lock1, lock2); 
return lhs_val  rhs_val ? true : false; 
} 
Минимизация 
гранулярности 
блокировки! 
45
˒˼˿˻˹́˿˳˻˱̂̀˿˽˿̊̍̏VWGXQLTXHBORFN 
void pop_and_process() { 
std::unique_lockstd::mutex lock(mut); 
Widget data = queue.pop(); // получить элемент данных 
lock.unlock(); // освободить мьютекс 
super_widget result = process(data); // обработать данные 
lock.lock(); // опять захватить мьютекс 
output_result(data, result); // вывести результат 
} 
Минимизация блокировок! 
▪ блокировать данные, а не операции 
▪ удерживать мьютекс столько, сколько необходимо 
▫ тяжёлые операции (захват другого мьютекса, 
ввод/вывод и т.д.) - вне текущей критической секции 
46
˟˵˾˿˻́˱̃˾̌˺˳̌˸˿˳˹˿̃˼˿˷˶˾˾˱̐˹˾˹̇˹˱˼˹˸˱̇˹̐ 
class NetFacility { 
private: 
connect_handle connection; 
bool connection_flag; 
void open_connection() { 
connection = connect_manager.open(); 
} 
public: 
NetFacility(connect_info _info): {} 
void send_data(data_packet const d) { 
// отложенная инициализация 
if (connection_flag == false) 
connection = open_connection(); 
connection.send(data); 
} 
void recv_data() { /* ... */ } 
} А если несколько 
потоков? 
47
˟˵˾˿˻́˱̃˾̌˺˳̌˸˿˳˹˿̃˼˿˷˶˾˾˱̐˹˾˹̇˹˱˼˹˸˱̇˹̐ 
class NetFacility { 
private: 
connect_handle connection; 
bool connection_flag; 
std::mutex mut; 
void open_connection() { 
connection = connect_manager.open(); 
} 
public: 
NetFacility(connect_info _info): {} 
void send_data(data_packet const d) { 
std::unique_lockstd::mutex lock(mut); 
if (connection_flag == false) 
// только инициализация требует защиты! 
connection = open_connection(); 
mut.unlock(); 
connection.send(data); 
} 
void recv_data() { /* ... */ } }; 
Защищать только 
инициализацию 
48
˟˵˾˿˻́˱̃˾̌˺˳̌˸˿˳˹˿̃˼˿˷˶˾˾˱̐˹˾˹̇˹˱˼˹˸˱̇˹̐ 
class NetFacility { 
private: 
connect_handle connection; 
bool connection_flag; 
std::mutex mut; 
void open_connection() { 
connection = connect_manager.open(); 
} 
public: 
NetFacility(connect_info _info): {} 
void send_data(data_packet const d) { 
if (connection_flag == false) { // гонка! 
std::lock_guardstd::mutex lock(mut); 
if (connection_flag == false) { 
connection = open_connection(); 
connection_flag = true; // гонка! 
} 
} 
connection.send(data); 
} 
двойная проверка 
49
˟˵˾˿˻́˱̃˾̌˺˳̌˸˿˳˹˿̃˼˿˷˶˾˾˱̐˹˾˹̇˹˱˼˹˸˱̇˹̐ 
class NetFacility { 
private: 
connect_handle connection; 
std::once_flag connection_flag; 
void open_connection() { 
connection = connect_manager.open(info); 
} 
public: 
NetFacility(connect_info _info): {} 
void send_data(data_packet const d) { 
// вызывается только один раз 
std::call_once(connection_flag, 
NetFacility::open_connection, this); 
connection.send(data); 
} 
void recv_data() { /* ... */ } 
} 
50
5:˽̍̏̃˶˻̂̌˳ˢ 
class Widget { 
mutable std::shared_timed_mutex mut; 
int data; 
public: 
Widget operator=(const R rhs) { 
// эксклюзивные права на запись в *this 
std::unique_lockstd::shared_timed_mutex 
lhs(mut, std::defer_lock); 
// разделяемые права на чтение rhs 
std::shared_lockstd::shared_timed_mutex 
rhs(other.mut, std::defer_lock); 
std::lock(lhs, rhs); 
// выполнить присваивание 
data = rhs.data; 
return *this; 
} 
}; 
51
5:˽̍̏̃˶˻̂̌˳ˢ 
int Widget::read() { 
std::shared_lockshared_timed_mutex lock(mut); 
return val; 
} 
void Widget::set_value(int _val) { 
std::lock_guardshared_mutex lock(mut); 
val = _val; 
} 
52
ˡ˶˻̄́̂˹˳˾̌˶˽̍̏̃˶˻̂̌ 
▪ std::recursive_mutex 
▪ мьютекс можно запирать несколько раз в 
одном потоке 
▪ освобождать мьютекс требуется столько 
раз, сколько он был захвачен 
▪ использование - аналогично std::mutex 
(std::lock_guard, std::unique_lock, 
…) 
53
ˤ̂˼˿˳˾̌˶̀˶́˶˽˶˾˾̌˶ 
▪ std::condition_variable, std:: 
condition_variable_any 
условная переменная, необходимо взаимодействие с 
мьютексом (condition_variable) или с любым 
классом (condition_variable_any), подобным 
мьютексу 
▪ wait - ожидание условия 
▪ wait_for, wait_until - ожидание условия заданное 
время или до заданного момента 
▪ notify_one - сообщить одному потоку 
▪ notify_all - сообщить всем потокам 
54
ˤ̂˼˿˳˾̌˶̀˶́˶˽˶˾˾̌˶̀́˿˹˸˳˿˵˹̃˶˼̍̀˿̃́˶˲˹̃˶˼̍ 
std::mutex mut; 
std::queueWidget widget_queue; 
std::condition_variable cond; 
void producer() { 
for (;;) { 
Widget const w = get_request(); 
std::lock_guardstd::mutex lock(mut); 
widget_queue.push(data); 
cond.notify_one(); 
} } 
void consumer() { 
for (;;) { 
std::unique_lockstd::mutex lock(mut); 
cond.wait(lock, []{return !widget_queue.empty();}); 
Widget w = widget_queue.pop(); 
lock.unlock(); 
process(widget); 
} } 55
˒̄˵̄̊˹˶́˶˸̄˼̍̃˱̃̌ 
56
˒̄˵̄̊˹˶́˶˸̄˼̍̃˱̃̌IXWXUH
std::future  std::shared_future 
57
˒̄˵̄̊˹˶́˶˸̄˼̍̃˱̃̌IXWXUH
int thinking(); 
// Запуск асинхронной (“фоновой”) задачи 
std::futureint answer = std::async(thinking); 
// Работа основного потока 
do_other_stuff(); // в этом время работает thinking() 
// Получение результатов 
std::cout  The answer is   answer.get()  std::endl; 
main thread T1 
answer.get() 
thinking... T2 
работа ожидание 
async 
58
˒̄˵̄̊˹˶́˶˸̄˼̍̃˱̃̌IXWXUH
struct Widget { 
void foo(std::string const, int); 
int bar(std::string const); 
int operator()(int); 
}; 
Widget w; 
// Вызывается foo(carpe dieum, 2014) для объекта w 
auto f1 = std::async(Widget::foo, w, carpe diem, 2014); 
// Вызывается bar(carpe dieum, 2014) для объекта tmp = w 
auto f2 = std::async(Widget::bar, w, carpe diem); 
// Вызывается tmp.operator(2014), где tmp = w 
auto f3 = std::async(Widget(), 2014); 
// Вызвается w(1234) 
auto f4 = std::async(std::ref(w), 2014); 
59
˒̄˵̄̊˹˶́˶˸̄˼̍̃˱̃̌IXWXUH
struct Widget { 
Widget(); 
Widget(Widget); // Конструктор перемещения 
Widget(Widget const) = delete; // Запретить копирование 
// Оператор “перемещающее присваивание” 
Widget operator=(Widget); 
// Запретить присваивание 
Widget operator=(Widget const) = delete; 
void foo(std::string const, int); 
int bar(std::string const); 
int operator()(int); 
}; 
Widget w; 
auto f1 = std::async(Widget::foo, w, hi, 2014); 
auto f2 = std::async(Widget::bar, w, hi); 
auto f3 = std::async(Widget(), 2014); 
auto f4 = std::async(std::ref(w), 2014); 
60
˒̄˵̄̊˹˶́˶˸̄˼̍̃˱̃̌IXWXUH
▪ std::launch::async - запуск функции в 
асинхронном режиме 
▪ std::launch::deferred - запуск в момент вызова 
wait или get 
▪ std::launch::async | std::launch::deferred - 
на усмотрение реализации (по умолчанию) 
auto f5 = std::async(std::launch::deferred, 
Widget::foo(), carpe diem, 2014); 
auto f6 = std::async(std::launch::deferred, 
Widget::bar(), carpe diem); 
auto f7 = std::async(std::launch::async, Widget(), 2014); 
std::cout  f5.get()  std::endl; // вызывается foo() 
f6.wait(); // вызывается bar() 
std::cout  f7.get()  std::endl; // только ожидание 
// результата 61
ˤ̀˱˻˿˳˱˾˾̌˶˸˱˵˱̈˹ 
task 1 task 2 task 3 
package 1 package 2 package 3 
62
ˤ̀˱˻˿˳˱˾˾̌˶˸˱˵˱̈˹ 
▪ Шаблон std::packaged_task связывается будущий 
результат (future) с функцией 
▪ Вызов функции происходит при вызове объекта 
packaged_task 
▪ Параметр шаблона - сигнатура функции 
template 
class packaged_taskint(float, char) { 
public: 
templatetypename Callable 
explicit packaged_task(Callable func); 
std::futureint get_future(); 
void operator()(std::vectorchar*, int); 
}; 
пример 
спецификации 
шаблона для 
сигнатуры 
функции 
int func(float, 
char) 
63
ˤ̀˱˻˿˳˱˾˾̌˶˸˱˵˱̈˹̀́˹˽˶́̀̄˼˸˱˵˱̈
task 
package 
task 
package 
add_task batch_system 
tasks.push_back( 
std::move(task)); 
std::packaged_taskvoid() task 
= std::move(tasks.front()); 
64
ˤ̀˱˻˿˳˱˾˾̌˶˸˱˵˱̈˹̀́˹˽˶́ 
std::mutex mut; 
std::dequestd::packaged_taskvoid() tasks; 
bool exit_flag = false; 
bool is_exit() { 
std::mutex mut; 
std::lock_guardstd::mutex lock(mut); 
return exit_flag; 
} 
void batch_system() { 
while (!is_exit()) { 
std::unique_lockstd::mutex lock(mut); 
if (tasks.empty()) continue; 
std::packaged_taskvoid() task = // получить упакованную 
std::move(tasks.front()); // задачу из очереди 
tasks.pop_front(); // удалить из очереди 
lock.unlock(); 
task(); // запуск задачи 
} } 65
ˤ̀˱˻˿˳˱˾˾̌˶˸˱˵˱̈˹̀́˹˽˶́ 
templatetypename func 
std::futurevoid add_task(func f) 
{ 
std::packaged_taskvoid() task(f); 
std::futurevoid res = task.get_future(); 
std::lock_guardstd::mutex lock(mut); 
tasks.push_back(std::move(task)); 
return res; 
} 
void say_vox() { std::cout  voxn; } 
void say_populi() { std::cout  populin; } 
void say_dei() { std::cout  dein; } 
void write_word() { std::string s; std::cin  s; } 
66
ˤ̀˱˻˿˳˱˾˾̌˶˸˱˵˱̈˹̀́˹˽˶́ 
int main() { 
std::thread batch(batch_system); 
add_task(say_vox); 
add_task(say_populi); 
add_task(write_word); 
add_task(say_vox); 
add_task(say_dei); 
std::this_thread::sleep_for( 
std::chrono::milliseconds(1000)); 
std::mutex mut; 
std::unique_lockstd::mutex lock(mut); 
exit_flag = true; 
lock.unlock(); 
batch.join(); 
return 0; 
} 
67
ˤ̀˱˻˿˳˱˾˾̌˶˸˱˵˱̈˹̀́˹˽˶́˳˿˸˽˿˷˾̌˶˳˱́˹˱˾̃̌ 
$ ./prog 
vox 
populi 
, 
$ ./prog 
vox 
populi 
, 
vox 
dei 
$ ./prog 
vox 
populi 
, 
vox 
68
q˟˲˶̊˱˾˾̌˶ŕ˶˸̄˼̍̃˱̃̌VWGSURPLVH
̀́˹˽˶́ 
void print_value(std::futureint fut) { 
int x = fut.get(); 
std::cout  value:   x  std::endl; 
} 
int compute_value() { 
std::this_thread::sleep_for(std::chrono::seconds(1)); 
return 42; 
} 
int main () { 
std::promiseint prom; 
// Получаем объект future из созданного promise (обещаем) 
std::futureint fut = prom.get_future(); 
// Отправляем будущее значение в новый поток 
std::thread th1 (print_value, std::ref(fut)); 
int val = compute_value(); 
prom.set_value(val); // Выполняем обещание 
th1.join(); 
} 69
q˟˲˶̊˱˾˾̌˶ŕ˶˸̄˼̍̃˱̃̌VWGSURPLVH
̀́˹˽˶́ 
main thread main 
prom.set_value() 
compute_value 
print_value print_value 
th1 
th1(print_value, 
std::ref(fut)) 
fut.get() 
создание/завершение 
потоков синхронизация 
работа ожидание 
70
˞˶˵˿̂̃˱̃˻˹̂˹˾̆́̇˹˹˾˱˿̂˾˿˳˶̄̂˼˿˳˾̌̆̀˶́˶˽˶˾˾̌̆ 
std::mutex mut; 
std::queueWidget widget_queue; 
std::condition_variable cond; 
void producer() { 
for (;;) { 
Widget const w = get_request(); 
std::lock_guardstd::mutex lock(mut); 
widget_queue.push(data); 
cond.notify_one(); 
} } 
void consumer() { 
for (;;) { 
std::unique_lockstd::mutex lock(mut); 
cond.wait(lock, []{return !widget_queue.empty();}); 
Widget w = widget_queue.pop(); 
lock.unlock(); 
process(widget); 
} } 
71
˞˶˵˿̂̃˱̃˻˹̂˹˾̆́̇˹˹˾˱˿̂˾˿˳˶̄̂˼˿˳˾̌̆̀˶́˶˽˶˾˾̌̆ 
▪ “Код с запашком” (code smell) 
Например, потоки выполняют код, который не 
нуждается в блокировке мьютекса (например, один 
поток инициализирует структуру, после чего сообщает 
другому, что структура готова). 
▪ Пропущенный сигнал 
Поток может отправить сигнал (notify_one/all) в тот 
момент, когда другой поток не начал ожидать 
▪ Ложное пробуждение (spurious wakeup) 
Поток, ожидающий сигнала может проснуться тогда, 
когда сигнал не был отправлен (или когда он был 
отправлен не этому потоку, или когда условие перестало 
выполняться). Нужна дополнительная проверка! 
(return !widget_queue.empty();) 
А что, если он не может проверить?! 72
q˟˲˶̊˱˾˾̌˶ŕ˶˸̄˼̍̃˱̃̌VWGSURPLVH
p.get_future().wait() I'm waiting... 
std::promise... p std::future 
73
q˟˲˶̊˱˾˾̌˶ŕ˶˸̄˼̍̃˱̃̌VWGSURPLVH
p.get_future().wait() I'm waiting... 
ok, let’s move! 
p.set_value() 
std::promise... p std::future 
74
q˟˲˶̊˱˾˾̌˶ŕ˶˸̄˼̍̃˱̃̌VWGSURPLVH
p.get_future().wait() I'm waiting... 
Можно отправить 
сигнал только 
один раз 
ok, let’s move! 
p.set_value() 
std::promise... p std::future 
75
q˟˲˶̊˱˾˾̌˶ŕ˶˸̄˼̍̃˱̃̌VWGSURPLVH
̀́˹˽˶́ 
std::promisevoid p; 
void react(); // реакция на условие 
void detect() { // обнаружение условия 
std::thread t([] { 
p.get_future().wait() 
react(); 
}); 
// делаем что-то // в это время t спит 
p.set_value(); // разбудить t 
// делаем ещё что-то 
t.join(); 
}; 
76
q˟˲˶̊˱˾˾̌˶ŕ˶˸̄˼̍̃˱̃̌VWGSURPLVH
̀́˹˽˶́ 
std::promisevoid p; 
void react(); // реакция на условие 
void detect() { // обнаружение условия 
std::thread t([] { 
p.get_future().wait() 
react(); 
}); 
// а что, если здесь возникнет исключение?? 
p.set_value(); // разбудить t 
// делаем ещё что-то 
t.join(); 
}; 
77
˝˾˿˷˶̂̃˳˶˾˾˱̐˿̃̀́˱˳˻˱̂˹˴˾˱˼˿˳˹˼˹̀́˹˽˶́ 
std::promisevoid p; 
void detect() { 
auto sf = p.get_future().share(); 
std::vectorstd::thread vt; 
for (int i = 0; i  threadsToRun; i++) { 
vt.emplace_back([sf]{ sf.wait(); 
react(); }); 
} 
// ... 
p.set_value(); 
// ... 
for (auto t: vt) t.join(); 
}; 
78
q˟˲˶̊˱˾˾̌˶ŕ˶˸̄˼̍̃˱̃̌VWGSURPLVH
̀́˹˽˶́ 
int main() { 
std::istringstream iss_numbers{3 1 42 23 -23 93 2 -289}; 
std::istringstream iss_letters{ a 23 b,e k k?a;si,ksa c}; 
std::vectorint numbers; 
std::vectorchar letters; 
std::promisevoid numbers_promise, letters_promise; 
auto numbers_ready = numbers_promise.get_future(); 
auto letter_ready = letters_promise.get_future(); 
std::thread value_reader([]{ 
std::copy(std::istream_iteratorint{iss_numbers}, 
std::istream_iteratorint{}, 
std::back_inserter(numbers)); 
numbers_promise.set_value(); 
std::copy_if(std::istreambuf_iteratorchar{iss_letters}, 
std::istreambuf_iteratorchar{}, 
std::back_inserter(letters), ::isalpha); 
letters_promise.set_value(); 
}); 79
q˟˲˶̊˱˾˾̌˶ŕ˶˸̄˼̍̃˱̃̌VWGSURPLVH
̀́˹˽˶́ 
numbers_ready.wait(); // Ждать когда числа будут готовы 
std::sort(numbers.begin(), numbers.end()); 
if (letter_ready.wait_for(std::chrono::milliseconds(100)) == 
std::future_status::timeout) { 
// выводим числа, пока обрабатываются символы 
for (int num : numbers) std::cout  num  ' '; 
std::cout  'n'; 
numbers.clear(); // Числа уже были напечатаны 
} 
letter_ready.wait(); 
std::sort(letters.begin(), letters.end()); 
for (char let : letters) std::cout  let  ' '; 
std::cout  'n'; 
// If numbers were already printed, it does nothing. 
for (int num : numbers) std::cout  num  ' '; 
std::cout  'n'; 
value_reader.join(); 
} 80
q˟˲˶̊˱˾˾̌˶ŕ˶˸̄˼̍̃˱̃̌VWGSURPLVH
̀́˹˽˶́ 
letter_ready.wait_for 
main main 
iss_numbers iss_letters 
работа ожидание 
value_ 
reader 
letters_promise. 
value_reader set_value() 
fut.get() 
number_ready.wait() 
sort 
sort output 
numbers_promise. 
set_value() 
создание/завершение 
потоков синхронизация 
81
q˟˲˶̊˱˾˾̌˶ŕ˶˸̄˼̍̃˱̃̌VWGSURPLVH
˳˱́˹˱˾̃̌ 
a a a a b c e i k k k s s 
-289 -23 1 2 3 4 23 42 93 93 
-289 -23 1 2 3 23 42 93 
a a a b c e i k k k s s 
82
ˡ˱˸˵˶˼̐˶˽̌˶˲̄˵̄̊˹˶́˶˸̄˼̍̃˱̃̌VKDUHGBIXWXUH 
int main() { 
std::promisevoid ready_promise, t1_ready_promise, 
t2_ready_promise; 
std::shared_futurevoid 
ready_future(ready_promise.get_future()); 
std::chrono::time_pointstd::chrono::high_resolution_clock 
start; 
auto fun1 = []() - std::chrono::durationdouble, std::milli 
{ 
t1_ready_promise.set_value(); 
ready_future.wait(); // ожидать сигнала из main() 
return std::chrono::high_resolution_clock::now() - start; 
}; 
auto fun2 = []() - std::chrono::durationdouble, std::milli 
{ 
t2_ready_promise.set_value(); 
ready_future.wait(); // ожидать сигнала из main() 
return std::chrono::high_resolution_clock::now() - start; 
}; 83
ˡ˱˸˵˶˼̐˶˽̌˶˲̄˵̄̊˹˶́˶˸̄˼̍̃˱̃̌VKDUHGBIXWXUH 
auto result1 = std::async(std::launch::async, fun1); 
auto result2 = std::async(std::launch::async, fun2); 
// ждать, пока потоки не будут готовы 
t1_ready_promise.get_future().wait(); 
t2_ready_promise.get_future().wait(); 
// потоки готовы - начать отчёт времени 
start = std::chrono::high_resolution_clock::now(); 
// запустить потоки 
ready_promise.set_value(); 
std::cout  Thread 1 received the signal  
 result1.get().count()   ms after startn 
 Thread 2 received the signal  
 result2.get().count()   ms after startn; 
} 
84
ˡ˱˸˵˶˼̐˶˽̌˶˲̄˵̄̊˹˶́˶˸̄˼̍̃˱̃̌VKDUHGBIXWXUH 
main 
создание/завершение 
потоков синхронизация 
работа ожидание 
T1 
T2 
t2_ready. 
set_value 
start 
return 
return 
output 
ready. 
set_value 
t1_ready. 
set_value 
85
˒̌̂̃́˱̐̂˿́̃˹́˿˳˻˱˳˵̄̆˶̅̄˾˻̇˹˿˾˱˼̍˾˿˴˿̀́˿˴̐ 
86
˒̌̂̃́˱̐̂˿́̃˹́˿˳˻˱˳˵̄̆˶̅̄˾˻̇˹˿˾˱˼̍˾˿˴˿̀́˿˴̐ 
templatetypename T 
std::listT sequential_quick_sort(std::listT input) { 
if (input.empty()) { return input; } 
std::listT result; 
result.splice(result.begin(), input, input.begin()); 
T const pivot = *result.begin(); 
auto divide_point = std::partition(input.begin(), 
input.end(), [](T const t){return t  pivot; }); 
std::listT lower_part; 
lower_part.splice(lower_part.end(), input, input.begin(), 
divide_point); 
auto new_lower( 
sequential_quick_sort(std::move(lower_part))); 
auto new_higher( 
sequential_quick_sort(std::move(input))); 
result.splice(result.end(), new_higher); 
result.splice(result.begin(), new_lower); 
return result; } 87
˒̌̂̃́˱̐̂˿́̃˹́˿˳˻˱˳˵̄̆˶̅̄˾˻̇˹˿˾˱˼̍˾˿˴˿̀́˿˴̐ 
lower 
part 
input 
pivot 
input 
splice 
new_lower 
divide_point 
new_higher 
88
ˠ˱́˱˼˼˶˼̍˾̌˺˱˼˴˿́˹̃˽˲̌̂̃́˿˺̂˿́̃˹́˿˳˻˹ 
templatetypename T 
std::listT parallel_quick_sort(std::listT input) { 
if (input.empty()) { return input; } 
std::listT result; 
result.splice(result.begin(), input, input.begin()); 
T const pivot = *result.begin(); 
auto divide_point = std::partition(input.begin(), 
input.end(), [](T const t){return t  pivot; }); 
std::listT lower_part; 
lower_part.splice(lower_part.end(), input, input.begin(), 
divide_point); 
std::futurestd::listT new_lower( 
std::async(parallel_quick_sort, std::move(lower_part))); 
auto new_higher( 
parallel_quick_sort(std::move(input))); 
result.splice(result.end(), new_higher); 
result.splice(result.begin(), new_lower.get()); 
return result; } 89
˞˶˲˼˿˻˹́̄̏̊˹˶ 
˲̄˵̄̊˹˶́˶˸̄˼̍̃˱̃̌˹ 
˵́̄˴˹˶̀˶́̂̀˶˻̃˹˳˾̌˶ 
̀́˹˽˹̃˹˳̌ 
̂˹˾̆́˿˾˹˸˱̇˹˹ 
90
˞˶˲˼˿˻˹́̄̏̊˹˶˲̄˵̄̊˹˶́˶˸̄˼̍̃˱̃̌WKHQ
auto func1() { 
std::cout  begin thinking over the answer...n; 
std::this_thread::sleep_for(dur3); 
return 40; 
} 
auto func2(int x) { 
std::cout  continue thinking over the answer...n; 
std::this_thread::sleep_for(dur1); 
return x + 2; 
} 
auto func3(int x) { 
std::cout  still thinking...n; 
std::this_thread::sleep_for(dur2); 
return number  + std::to_string(x); 
} 
void do_some_stuff() { std::cout  do some useful stuff; } 
void do_some_other_stuff() { std::cout  do other stuff; } 
91
˞˶˲˼˿˻˹́̄̏̊˹˶˲̄˵̄̊˹˶́˶˸̄˼̍̃˱̃̌WKHQ
int main() { 
auto f1 = std::async(func1); 
auto f2 = std::async(func2, f1.get()); 
auto f3 = std::async(func3, f2.get()); 
std::cout  waiting for the answer...n; 
do_some_stuff(); 
std::cout  answer:   f3.get()  std::endl; 
do_some_other_stuff(); 
92
˞˶˲˼˿˻˹́̄̏̊˹˶˲̄˵̄̊˹˶́˶˸̄˼̍̃˱̃̌WKHQ
int main() { 
auto f1 = std::async(func1); 
auto f2 = std::async(func2, f1.get()); 
auto f3 = std::async(func3, f2.get()); 
std::cout  waiting for the answer...n; 
do_some_stuff(); 
std::cout  answer:   f3.get()  std::endl; 
do_some_other_stuff(); 
Каждый раз после получения результата выполняется 
создание новой асинхронной задачи. 
Поток может быть заблокирован при вызове get() для 
ожидания результата. 
93
˞˶˲˼˿˻˹́̄̏̊˹˶˲̄˵̄̊˹˶́˶˸̄˼̍̃˱̃̌WKHQ
$ ./prog 
begin thinking over the answer... 
continue thinking over the answer... 
waiting for the answer... 
do some useful stuff 
answer: still thinking... 
number 42 
do some other useful stuff 
94
˞˶˲˼˿˻˹́̄̏̊˹˶˲̄˵̄̊˹˶́˶˸̄˼̍̃˱̃̌WKHQ
#define BOOST_THREAD_PROVIDES_FUTURE 
#define BOOST_THREAD_PROVIDES_FUTURE_CONTINUATION 
#include boost/thread/future.hpp 
int main() { 
auto f = boost::async([](){ 
return func1(); 
}).then([](auto f){ 
return func2(f.get()); 
}).then([](auto f){ 
return func3(f.get()); 
}); 
std::cout  waiting for the answer...n; 
do_some_stuff(); 
f.then([](auto f){ 
std::cout  answer:   f.get()  std::endl; 
}); 
do_some_other_stuff(); 95
˞˶˲˼˿˻˹́̄̏̊˹˶˲̄˵̄̊˹˶́˶˸̄˼̍̃˱̃̌WKHQ
#define BOOST_THREAD_PROVIDES_FUTURE 
#define BOOST_THREAD_PROVIDES_FUTURE_CONTINUATION 
#include boost/thread/future.hpp 
int main() { 
auto f = boost::async([](){ 
return func1(); 
}).then([](auto f){ 
return func2(f.get()); 
}).then([](auto f){ 
return func3(f.get()); 
}); 
std::cout  waiting for the answer...n; 
do_some_stuff(); 
f.then([](auto f){ 
std::cout  answer:   f.get()  std::endl; 
вызывающий поток не блокируется 
}); 
do_some_other_stuff(); 
96
˞˶˲˼˿˻˹́̄̏̊˹˶˲̄˵̄̊˹˶́˶˸̄˼̍̃˱̃̌WKHQ
#define BOOST_THREAD_PROVIDES_FUTURE 
#define BOOST_THREAD_PROVIDES_FUTURE_CONTINUATION 
#include boost/thread/future.hpp 
int main() { 
auto f = boost::async([](){ 
return func1(); 
}).then([](auto f){ 
return func2(f.get()); 
}).then([](auto f){ 
return func3(f.get()); 
}); 
std::cout  waiting for the answer...n; 
do_some_stuff(); 
f.then([](auto f){ 
std::cout  answer:   f.get()  std::endl; 
вызывающий поток блокируется 
}).wait(); 
do_some_other_stuff(); 
97
˞˶˲˼˿˻˹́̄̏̊˹˶˲̄˵̄̊˹˶́˶˸̄˼̍̃˱̃̌WKHQ
$ g++ -Wall -pedantic -pthread -lboost_system  
-lboost_thread -std=c++14 -O2 prog.cpp -o prog 
$ ./prog 
waiting for the answer... 
do some useful stuff 
begin thinking over the answer... 
continue thinking over the answer... 
still thinking... 
answer: number 42 
do some other useful stuff 
98
˞˶˲˼˿˻˹́̄̏̊˹˶˲̄˵̄̊˹˶́˶˸̄˼̍̃˱̃̌WKHQ
Блокирующие future Неблокирующие future 
f2 
f3 
f1 
f 
▪ порядок выполнения 
▪ устанавливается явный 
неопределён 
порядок выполнения 
▪ возможны блокировки 
▪ нет блокировок 
▪ для каждой задачи 
▪ поток один 
создаётся отдельный поток 99
˟˷˹˵˱˾˹˶˳̌̀˿˼˾˶˾˹̐˳̂˶̆˸˱˵˱̈ZKHQBDOO
#define BOOST_THREAD_PROVIDES_FUTURE_WHEN_ALL_WHEN_ANY 
#include boost/thread/future.hpp 
std::vectorboost::futurevoid task_chunk; 
task_chunk.emplace_back(boost::async([]() 
{ std::cout  hello from task 1n; })); 
task_chunk.emplace_back(boost::async([]() 
{ std::cout  hello from task 2n; })); 
task_chunk.emplace_back(boost::async([]() 
{ std::cout  hello from task 3n; })); 
auto join_task = boost::when_all(task_chunk.begin(), 
task_chunk.end()); 
do_some_stuff(); 
join_task.wait(); 
100
˟˷˹˵˱˾˹˶˳̌̀˿˼˾˶˾˹̐˳̂˶̆˸˱˵˱̈ZKHQBDOO
f2 
f1 
f3 
f4 
Будущий результат f4 зависит от выполнения всех 
будущих результатов f1, f2, f3 и начинает выполняться 
после завершения выполнения задач, им соответствующих 
(подобно барьерной синхронизации). 
101
˟˷˹˵˱˾˹˶˳̌̀˿˼˾˶˾˹̐˳̂˶̆˸˱˵˱̈ZKHQBDOO
std::vectorboost::futureint task_chunk; 
task_chunk.emplace_back(boost::async(boost::launch::async, 
[](){ std::cout  hello from task 1n; return 10; })); 
task_chunk.emplace_back(boost::async(boost::launch::async, 
[](){ std::cout  hello from task 2n; return 20; })); 
task_chunk.emplace_back(boost::async(boost::launch::async, 
[](){ std::cout  hello from task 3n; return 12; })); 
auto join_task = boost::when_all(task_chunk.begin(), 
task_chunk.end()) 
.then([](auto results){ 
auto res = 0; 
for (auto elem: results.get()) 
res += elem.get(); 
return res; 
join_task 
имеет тип 
future 
vector 
}); 
do_some_stuff(); 
std::cout  result:   join_task.get()  std::endl; 
futureT 
102
˟˷˹˵˱˾˹˶˳̌̀˿˼˾˶˾˹̐˳̂˶̆˸˱˵˱̈ZKHQBDOO
f2 
f1 
f3 
f4 
f5 
103
˟˷˹˵˱˾˹˶˳̌̀˿˼˾˶˾˹̐˳̂˶̆˸˱˵˱̈ZKHQBDOO
$ g++ -Wall -pedantic -pthread -lboost_system  
-lboost_thread -std=c++14 -O2 prog.cpp -o prog 
$ ./prog 
hello from task 1 
hello from task 3 
hello from task 2 
do some useful stuff 
result: 42 
104
˟˷˹˵˱˾˹˶˳̌̀˿˼˾˶˾˹̐˻˱˻˿˺˼˹˲˿˸˱˵˱̈˹ZKHQBDQ
std::vectorboost::futuredecltype(M_PI) task_chunk; 
task_chunk.emplace_back(boost::async(boost::launch::async, 
[]() { std::this_thread::sleep_for(dur1); return M_PI; })); 
task_chunk.emplace_back(boost::async(boost::launch::async, 
[]() { std::this_thread::sleep_for(dur2); return M_E; })); 
task_chunk.emplace_back(boost::async(boost::launch::async, 
[]() { std::this_thread::sleep_for(dur3); return M_LN2; })); 
auto join_task = boost::when_any(task_chunk.begin(), 
task_chunk.end()) 
.then([](auto results) { 
for (auto elem: results.get()) { 
if (elem.is_ready()) { return elem.get(); } 
} 
exit(1); // this will never happen 
}); 
do_some_stuff(); 
std::cout  result:   join_task.get()  std::endl; 
105
˟˷˹˵˱˾˹˶˳̌̀˿˼˾˶˾˹̐˻˱˻˿˺˼˹˲˿˸˱˵˱̈˹ZKHQBDQ
f2 
f1 
f3 
f4 
Будущий результат f4 зависит от выполнения одного из 
будущих результатов f1, f2, f3 и начинает выполняться 
после завершения выполнения хотя бы одной задачи 
(подобно синхронизации “эврика”). 
106
˟˷˹˵˱˾˹˶˳̌̀˿˼˾˶˾˹̐˻˱˻˿˺˼˹˲˿˸˱˵˱̈˹ZKHQBDQ
$ g++ -Wall -pedantic -pthread -lboost_system  
-lboost_thread -std=c++14 -O2 prog.cpp -o prog 
do some useful stuff 
result: 2.71828 
do some useful stuff 
result: 0.693147 
do some useful stuff 
result: 3.14159 
107
ˡ˶˱˼˹˸˱̇˹̐ 
˳̌̂˿˻˿̄́˿˳˾˶˳̌̆ 
̂́˶˵̂̃˳̂˹˾̆́˿˾˹˸˱̇˹˹ 
108
˝̍̏̃˶˻̂̌˹˿̈˶́˶˵˹˸˱˵˱̈ 
Блокировка мьютекса Очереди задач 
class Logger { 
std::fstream flog; 
public: 
void writelog(...) { 
flog  current_time() 
 :  logmsg 
 std::endl; 
} 
}; 
class Logger { 
std::fstream flog; 
public: 
void writelog(...) { 
flog  current_time() 
 :  logmsg 
 std::endl; 
} 
}; 
109
˝̍̏̃˶˻̂̌˹˿̈˶́˶˵˹˸˱˵˱̈ 
Блокировка мьютекса Очереди задач 
class Logger { 
std::fstream flog; 
std::mutex mut; 
public: 
void writelog(...) { 
std::lock_guard 
std::mutex lock(mut); 
flog  current_time() 
 :  logmsg 
 std::endl; 
} 
}; 
class Logger { 
std::fstream flog; 
worker_thread worker; 
public: 
void writelog(...) { 
worker.send([=]{ 
flog  current_time() 
 :  logmsg 
 std::endl; 
}); 
} 
}; 
110
˝̍̏̃˶˻̂̌˹˿̈˶́˶˵˹˸˱˵˱̈ 
Блокировка мьютекса 
▪ потоки блокируются 
▪ имеется возможность 
дедлока 
▪ небольшая 
масштабируемость 
▪ порядок следования 
сообщения в логе отличается 
от последовательности 
поступления 
Очереди задач 
▪ потоки не блокируются 
▪ отсутствует возможность 
дедлока 
▪ высокая масштабируемость 
▪ порядок следования 
сообщения в логе совпадает 
с фактическим 
111
ˠ˱̃̃˶́˾̀˿̃˿˻˿˲˶˸˿̀˱̂˾˱̐˿˲̒́̃˻˱˾˱˵˵˱˾˾̌˽˹ 
Требования к потокобезопасным “обёрткам”: 
1. Сохранение интерфейса 
widget w; = w.func(hi folks!); 
wrapperwidget = w.func(hi folks!); 
2. Универсальность. Заранее может быть неизвестны 
методы, которые необходимо обернуть. Некоторые методы 
сложно обернуть: конструкторы, операторы, шаблоны и т.д. 
3. Поддержка транзакций 
account.deposit(Sergey, 1000) 
account.withdraw(Ivan, 1000); 
log.print(user , username, data ); 
log.print(time , logmsg); 
Реализация отдельных методов может не обеспечить 
необходимую гранулярность. 
112
ˠ˱̃̃˶́˾˿˲̒́̃˻˱˾˱˵˵˱˾˾̌˽˹̂˲˼˿˻˹́˿˳˻˱˽˹ 
templatetypename T 
class wrapper { 
private: 
T t; // оборачиваемый объект 
... // состояние враппера 
public: 
monitor(T _t): t(_t) { } 
template typename F 
// 1. получаем любую функцию 
// 2. подставляем в неё оборачиваемый объект 
// 3. выполняем и возвращаем результат 
auto operator()(F f) - decltype(f(t)) { 
// работа враппера 
auto ret = f(t); 
// ... 
return ret; 
} 
}; 113
ˠ˿̃˿˻˿˲˶˸˿̀˱̂˾˱̐˿˲̒́̃˻˱˾˱˵˵˱˾˾̌˽˹̂˲˼˿˻˹́˿˳˻˱˽˹ 
templatetypename T 
class monitor { 
private: 
T t; 
std::mutex m; 
public: 
monitor(T _t): t(_t) { } 
template typename F 
auto operator()(F f) - decltype(f(t)) { 
std::lock_guardstd::mutex lock(m); 
// вызов “объявления” под защитой мьютекса 
return f(t); 
} 
}; 
114
ˠ˿̃˿˻˿˲˶˸˿̀˱̂˾˱̐˿˲̒́̃˻˱˾˱˵˵˱˾˾̌˽˹̂˲˼˿˻˹́˿˳˻˱˽˹ 
monitorstd::string smon{start }; // инициализация 
std::vectorstd::futurevoid v; 
for (auto i = 0; i  5; i++) { // выполнить асинхр. задачи... 
v.emplace_back(std::async(std::launch::async, [, i]{ 
smon([=](auto s){ // объявление функции 
s += i =  + std::to_string(i); 
s +=  ; 
}); 
smon([](auto s){ // объявление функции 
std::cout  s  std::endl; 
}); 
})); 
} 
for (auto f: v) // дождаться завершения 
f.wait(); 
std::cout  donen; 
115
ˠ˿̃˿˻˿˲˶˸˿̀˱̂˾˱̐˿˲̒́̃˻˱˾˱˵˵˱˾˾̌˽˹̂˲˼˿˻˹́˿˳˻˱˽˹ 
start i = 1 
start i = 1 i = 0 
start i = 1 i = 0 i = 2 
start i = 1 i = 0 i = 2 i = 4 
start i = 1 i = 0 i = 2 i = 4 i = 3 
done 
start i = 0 
start i = 0 i = 2 
start i = 0 i = 2 i = 3 
start i = 0 i = 2 i = 3 i = 1 
start i = 0 i = 2 i = 3 i = 1 i = 4 
done 
start i = 0 
start i = 0 i = 2 
start i = 0 i = 2 i = 4 
start i = 0 i = 2 i = 4 i = 1 
start i = 0 i = 2 i = 4 i = 1 i = 3 
done 116
ˠ˿̃˿˻˿˲˶˸˿̀˱̂˾˱̐˿˲̒́̃˻˱˾˱˵˵˱˾˾̌˽˹̂˲˼˿˻˹́˿˳˻˱˽˹ 
monitorstd::ostream mon_cout{std::cout}; 
std::vectorstd::futurevoid v; 
for (auto i = 0; i  5; i++) { 
v.emplace_back(std::async(std::launch::async, [, i]{ 
mon_cout([=](auto cout){ 
cout  i =   std::to_string(i); 
cout  n; 
}); 
mon_cout([=](auto cout){ 
cout  hi from   i  std::endl; 
}); 
})); 
} 
for (auto f: v) 
f.wait(); 
mon_cout([](auto cout){ 
cout  donen; 
}); 117
ˠ˿̃˿˻˿˲˶˸˿̀˱̂˾˱̐˿˲̒́̃˻˱˾˱˵˵˱˾˾̌˽˹̂˲˼˿˻˹́˿˳˻˱˽˹ 
i = 0 
i = 2 
hi from 2 
hi from 0 
i = 1 
hi from 1 
i = 3 
hi from 3 
i = 4 
hi from 4 
done 
i = 0 
i = 3 
i = 2 
hi from 2 
i = 1 
hi from 1 
hi from 3 
i = 4 
hi from 4 
hi from 0 
done 
i = 0 
hi from 0 
i = 4 
hi from 4 
i = 2 
hi from 2 
i = 3 
hi from 3 
i = 1 
hi from 1 
done 
i = 0 
hi from 0 
i = 2 
hi from 2 
i = 3 
hi from 3 
i = 1 
hi from 1 
i = 4 
hi from 4 
done 
118
ˠ˿̃˿˻˿˲˶˸˿̀˱̂˾˱̐˿˲̒́̃˻˱˾˱˵˵˱˾˾̌˽˹˾˱˿̂˾˿˳˶˿̈˶́˶˵˹˸˱˵˱̈ 
templatetypename T class concurrent { 
private: 
T t; 
concurrent_queuestd::functionvoid() q; 
bool done = false; 
std::thread thd; 
public: 
concurrent(T t_): t{t_}, thd{[=]{ 
while (!done) { 
(*q.wait_and_pop())(); 
} 
} } { } 
~concurrent() 
{ q.push([=]{ done = true; }); 
thd.join(); } 
templatetypename F void operator()(F f) 
{ q.push([=]{ f(t); }); } 
}; 119
ˠ˿̃˿˻˿˲˶˸˿̀˱̂˾˱̐˿˲̒́̃˻˱˾˱˵˵˱˾˾̌˽˹˾˱˿̂˾˿˳˶˿̈˶́˶˵˹˸˱˵˱̈ 
concurrentstd::string smon{start }; // инициализация 
std::vectorstd::futurevoid v; 
for (auto i = 0; i  5; i++) { // выполнить асинхр. задачи... 
v.emplace_back(std::async(std::launch::async, [, i]{ 
smon([=](auto s){ // объявление функции 
s += i =  + std::to_string(i); 
s +=  ; 
}); 
smon([](auto s){ // объявление функции 
std::cout  s  std::endl; 
}); 
})); 
} 
for (auto f: v) // дождаться завершения 
f.wait(); 
std::cout  donen; 
120
ˠ˿̃˿˻˿˲˶˸˿̀˱̂˾˱̐˿˲̒́̃˻˱˾˱˵˵˱˾˾̌˽˹˾˱˿̂˾˿˳˶˿̈˶́˶˵˹˸˱˵˱̈ 
start i = 0 
start i = 0 i = 2 
start i = 0 i = 2 i = 3 
start i = 0 i = 2 i = 3 i = 1 
start i = 0 i = 2 i = 3 i = 1 i = 4 
done 
start i = 0 
done 
start i = 0 i = 2 
start i = 0 i = 2 i = 1 
start i = 0 i = 2 i = 1 i = 3 
start i = 0 i = 2 i = 1 i = 3 i = 4 
start i = 0 
start i = 0 i = 1 
start i = 0 i = 1 i = 4 
start i = 0 i = 1 i = 4 i = 3 
start i = 0 i = 1 i = 4 i = 3 i = 2 
done 121
ˠ˿̃˿˻˿˲˶˸˿̀˱̂˾˱̐˿˲̒́̃˻˱˾˱˵˵˱˾˾̌˽˹˾˱˿̂˾˿˳˶˿̈˶́˶˵˹˸˱˵˱̈ 
concurrentstd::ostream mon_cout{std::cout}; 
std::vectorstd::futurevoid v; 
for (auto i = 0; i  5; i++) { 
v.emplace_back(std::async(std::launch::async, [, i]{ 
mon_cout([=](auto cout){ 
cout  i =   std::to_string(i); 
cout  n; 
}); 
mon_cout([=](auto cout){ 
cout  hi from   i  std::endl; 
}); 
})); 
} 
for (auto f: v) 
f.wait(); 
mon_cout([](auto cout){ 
cout  donen; 
}); 122
ˠ˿̃˿˻˿˲˶˸˿̀˱̂˾˱̐˿˲̒́̃˻˱˾˱˵˵˱˾˾̌˽˹˾˱˿̂˾˿˳˶˿̈˶́˶˵˹˸˱˵˱̈ 
i = 0 
hi from 0 
i = 2 
hi from 2 
i = 3 
hi from 3 
i = 4 
hi from 4 
i = 1 
hi from 1 
done 
i = 0 
hi from 0 
i = 2 
hi from 2 
i = 3 
hi from 3 
i = 4 
hi from 4 
i = 1 
hi from 1 
done 
i = 0 
i = 1 
hi from 1 
hi from 0 
i = 2 
hi from 2 
i = 4 
hi from 4 
i = 3 
hi from 3 
done 
i = 0 
hi from 0 
i = 2 
hi from 2 
i = 1 
i = 3 
hi from 3 
hi from 1 
i = 4 
hi from 4 
done 
123
ˠ˿̃˿˻˿˲˶˸˿̀˱̂˾˱̐˿˲̒́̃˻˱˾˱˵˵˱˾˾̌˽˹˾˱˿̂˾˿˳˶˿̈˶́˶˵˹˸˱˵˱̈ 
Данная версия operator() позволяет вернуть значение при вызове 
функции: 
templatetypename F 
auto operator()(F f) - std::futuredecltype(f(t)) { 
// создаём объект promise (shared_ptrpromise) 
auto p = std::make_sharedstd::promisedecltype(f(t))(); 
// получаем из promise объект future 
auto ret = p-get_future(); 
q.push([=]{ 
// выполняем обещание уже внутри потока 
try { p-set_value(f(t)); } 
catch (...) 
{ p-set_exception(std::current_exception()); } 
}); 
return ret; 
} 
124
ˠ˿̃˿˻˿˲˶˸˿̀˱̂˾˱̐˿˲̒́̃˻˱˾˱˵˵˱˾˾̌˽˹˾˱˿̂˾˿˳˶˿̈˶́˶˵˹˸˱˵˱̈ 
templatetypename F 
auto operator()(F f) - std::futuredecltype(f(t)) { 
auto p = std::make_sharedstd::promisedecltype(f(t))(); 
auto ret = p-get_future(); 
q.push([=]{ 
try { set_value(*p, f, t); } 
catch (...) 
{ p-set_exception(std::current_exception()); } 
}); 
return ret; 
} 
templatetypename Fut, typename F, typename T1 
void set_value(std::promiseFut p, F f, T1 t) 
{ p.set_value(f(t)); } 
templatetypename F, typename T1 
void set_value(std::promisevoid p, F f, T1 t) 
{ f(t); p.set_value(); } 
125
ˠ˿̃˿˻˿˲˶˸˿̀˱̂˾˱̐˿˲̒́̃˻˱˾˱˵˵˱˾˾̌˽˹˾˱˿̂˾˿˳˶˿̈˶́˶˵˹˸˱˵˱̈ 
templatetypename F 
auto operator()(F f) - std::futuredecltype(f(t)) { 
auto p = std::make_sharedstd::promisedecltype(f(t))(); 
auto ret = p-get_future(); 
q.push([=]{ 
try { set_value(*p, f, t); } 
catch (...) 
{ p-set_exception(std::current_exception()); } 
}); 
return ret; 
} 
auto f = smon([](auto s){ 
s += donen; 
std::cout  s; 
return s; 
}); 
std::cout  return value:   f.get()  std::endl; 126
ˠ˿̃˿˻˿˲˶˸˿̀˱̂˾˱̐˿˲̒́̃˻˱˾˱˿̂˾˿˳˶˿̈˶́˶˵˹˸˱˵˱̈̀́˹˽˶˾˶˾˹˶ 
class backgrounder { 
public: 
futurebool save(std::string file) { 
c([=](data d) { 
... // каждая функция - в отдельной транзакции 
}); 
} 
futuresize_t print(some stuff) { 
c([=, stuff](data d) { 
... // атомарный неделимый вывод 
}); 
} 
private: 
struct data { /* ... */ } // данные 
concurrentdata c; // обёртка для потокобезопасного 
}; // выполнения операций с данными 
127
ˡ˱˸́˱˲˿̃˻˱ 
̀˱́˱˼˼˶˼̍˾̌̆̂̃́̄˻̃̄́ 
˵˱˾˾̌̆̂˲˼˿˻˹́˿˳˻˱˽˹ 
128
˧˶˼̍́˱˸́˱˲˿̃˻˹̀˱́˱˼˼˶˼̍˾̌̆̂̃́̄˻̃̄́˵˱˾˾̌̆ 
▪ Обеспечить параллельный доступ 
▪ Обеспечить безопасность доступа 
▪ Минимизировать взаимные исключения 
▪ Минимизировать сериализацию 
129
˧˶˼̍́˱˸́˱˲˿̃˻˹̀˱́˱˼˼˶˼̍˾̌̆̂̃́̄˻̃̄́˵˱˾˾̌̆ 
Задачи проектирования структур данных с блокировками: 
▪ Ни один поток не может увидеть состояние, в котором 
инварианты нарушены 
▪ Предотвратить состояние гонки 
▪ Предусмотреть возникновение исключений 
▪ Минимизировать возможность взаимоблокировок 
Средства достижения: 
▪ ограничить область действия блокировок 
▪ защитить разные части структуры разными 
мьютексами 
▪ обеспечить разный уровень защиты 
▪ изменить структуру данных для расширения 
возможностей распраллеливания 130
ˠ˿̃˿˻˿˲˶˸˿̀˱̂˾̌˺̂̃˶˻̀˿̃˶˾̇˹˱˼̍˾̌˶̀́˿˲˼˶˽̌ 
Потенциальные проблемы безопасности реализации 
потокобезопасных структур: 
1. Гонки данных 
2. Взаимоблокировки 
3. Безопасность относительно исключений 
4. ... 
131
ˠ˿̃˿˻˿˲˶˸˿̀˱̂˾̌˺̂̃˶˻ 
struct empty_stack: std::exception { }; 
templatetypename T 
class threadsafe_stack { 
private: 
std::stackT data; 
mutable std::mutex m; 
public: 
threadsafe_stack() {} 
threadsafe_stack(const threadsafe_stack other) { 
std::lock_guardstd::mutex lock(other.m); 
data = other.data; 
} 
threadsafe_stack operator=(const threadsafe_stack) = delete; 
void push(T new_value) { 
std::lock_guardstd::mutex lock(m); 
data.push(std::move(new_value)); 
} 
Защита 
данных 
132
ˠ˿̃˿˻˿˲˶˸˿̀˱̂˾̌˺̂̃˶˻ 
T pop() { 
std::lock_guardstd::mutex lock(m); 
if (data.empty()) throw empty_stack(); 
auto value = data.top(); 
data.pop(); 
return value; 
} 
bool empty() const { 
std::lock_guardstd::mutex lock(m); 
return data.empty(); 
} 
}; 
133
ˠ˿̃˿˻˿˲˶˸˿̀˱̂˾̌˺̂̃˶˻̃˶̂̃˿˳˱̐̀́˿˴́˱˽˽˱ 
threadsafe_stackint stack; 
void pusher(unsigned nelems) { 
for (unsigned i = 0; i  nelems; i++) { stack.push(i); } 
} 
void printer() { 
try { 
for (;;) { int val; stack.pop(val); } 
} 
catch (empty_stack) { 
std::cout  stack is empty!  std::endl; 
} 
} 
int main() { 
std::thread t1(pusher, 5), t2(pusher, 5); 
t1.join(); t2.join(); 
std::thread t3(printer); 
t3.join(); 
} 134
ˠ˿̃˿˻˿˲˶˸˿̀˱̂˾̌˺̂̃˶˻˲˶˸˿̀˱̂˾˿̂̃̍˹̂˻˼̏̈˶˾˹˺ 
T pop() { 
1 
std::lock_guardstd::mutex lock(m); 
if (data.empty()) throw empty_stack(); 
auto value = data.top(); 
data.pop(); 
return value; 
} 
2 
3 
[невозвратная] модификация контейнера 
4 
135
˓˶́̂˹̐SRS˲˶˸˿̀˱̂˾˱̐̂̃˿̈˻˹˸́˶˾˹̐˹̂˻˼̏̈˶˾˹˺ 
std::shared_ptrT pop() { 
std::lock_guardstd::mutex lock(m); 
if (data.empty()) throw empty_stack(); 
std::shared_ptrT const res( 
std::make_sharedT(std::move(data.top()))); 
data.pop(); 
return res; 
} 
void pop(T value) { 
[невозвратная] модификация контейнера 
std::lock_guardstd::mutex lock(m); 
if (data.empty()) throw empty_stack(); 
value = std::move(data.top()); 
data.pop(); 
} 
1 
2 
3 
4 
5 
6 
[невозвратная] модификация контейнера 
136
ˠ˿̃˿˻˿˲˶˸˿̀˱̂˾̌˺̂̃˶˻˳˸˱˹˽˿˲˼˿˻˹́˿˳˻˹ 
struct empty_stack: std::exception { }; 
templatetypename T 
class threadsafe_stack { 
private: 
std::stackT data; 
mutable std::mutex m; 
public: 
threadsafe_stack() {} 
threadsafe_stack(const threadsafe_stack other) { 
std::lock_guardstd::mutex lock(other.m); 
data = other.data; 
} 
threadsafe_stack operator=(const threadsafe_stack) = delete; 
void push(T new_value) { 
std::lock_guardstd::mutex lock(m); 
data.push(std::move(new_value)); 
} 
'($'/2.  
137
ˠ˿̃˿˻˿˲˶˸˿̀˱̂˾̌˺̂̃˶˻˳˸˱˹˽˿˲˼˿˻˹́˿˳˻˹ 
std::shared_ptrT pop() { 
std::lock_guardstd::mutex lock(m); 
if (data.empty()) throw empty_stack(); 
std::shared_ptrT const res( 
std::make_sharedT(std::move(data.top()))); 
data.pop(); 
return res; 
} 
void pop(T value) { 
std::lock_guardstd::mutex lock(m); 
if (data.empty()) throw empty_stack(); 
value = std::move(data.top()); 
data.pop(); 
} 
bool empty() const { 
std::lock_guardstd::mutex lock(m); 
return data.empty(); 
} 
}; 
'($'/2.  
'($'/2.  
138
ˠ˿̃˿˻˿˲˶˸˿̀˱̂˾̌˺̂̃˶˻̃˶̂̃˿˳˱̐̀́˿˴́˱˽˽˱ 
threadsafe_stackint stack; 
void pusher(unsigned nelems) { 
for (unsigned i = 0; i  nelems; i++) { stack.push(i); } 
} 
void printer() { 
try { 
for (;;) { int val; stack.pop(val); } 
} 
catch (empty_stack) { 
std::cout  stack is empty!  std::endl; 
} 
} 
int main() { 
std::thread t1(pusher, 5), t2(pusher, 5); 
t1.join(); t2.join(); 
std::thread t3(printer); 
t3.join(); 
} 
Недостатки реализации: 
▪ Сериализация потоков приводит к снижению 
производительности: потоки простаивают и не 
совершают полезной работы 
▪ Нет средств, позволяющих ожидать добавления 
элемента 
139
ˠ˿̃˿˻˿˲˶˸˿̀˱̂˾˱̐˿̈˶́˶˵̍̂˿˷˹˵˱˾˹˶˽ 
templatetypename T class threadsafe_queue { 
private: 
mutable std::mutex mut; 
std::queueT data_queue; 
std::condition_variable data_cond; 
public: 
threadsafe_queue() {} 
void push(T new_value) { 
std::lock_guardstd::mutex lk(mut); 
data_queue.push(std::move(new_value)); 
data_cond.notify_one(); 
} 
std::shared_ptrT wait_and_pop() { 
std::unique_lockstd::mutex lk(mut); 
data_cond.wait(lk, [this]{return !data_queue.empty();}); 
std::shared_ptrT res( 
std::make_sharedT(std::move(data_queue.front()))); 
data_queue.pop(); 
return res; 
} 
140
ˠ˿̃˿˻˿˲˶˸˿̀˱̂˾˱̐˿̈˶́˶˵̍̂˿˷˹˵˱˾˹˶˽ 
void wait_and_pop(T value) { 
std::unique_lockstd::mutex lk(mut); 
data_cond.wait(lk, [this]{return !data_queue.empty();}); 
value = std::move(data_queue.front()); 
data_queue.pop(); 
} 
bool try_pop(T value) { 
std::lock_guardstd::mutex lk(mut); 
if (data_queue.empty()) return false; 
value = std::move(data_queue.front()); 
data_queue.pop(); 
return true; 
} 
std::shared_ptrT try_pop() { 
// ... 
} 
bool empty() const { /* ... */ } 
141
ˠ˿̃˿˻˿˲˶˸˿̀˱̂˾˱̐˿̈˶́˶˵̍̃˶̂̃˿˳˱̐̀́˿˴́˱˽˽˱ 
threadsafe_queueint queue; 
void pusher(unsigned nelems) { 
for (unsigned i = 0; i  nelems; i++) { 
queue.push(i); 
} 
} 
void poper(unsigned nelems) { 
for (unsigned i = 0; i  nelems; i++) { 
int val; 
queue.wait_and_pop(val); 
} 
} 
int main() { 
std::thread t1(pusher, 5), t2(pusher, 5), t3(poper, 9); 
t1.join(); 
t2.join(); 
t3.join(); 
} 
Не требуется 
проверка empty() 
142

More Related Content

What's hot

ПВТ - весна 2015 - Лекция 2. POSIX Threads. Основные понятия многопоточного п...
ПВТ - весна 2015 - Лекция 2. POSIX Threads. Основные понятия многопоточного п...ПВТ - весна 2015 - Лекция 2. POSIX Threads. Основные понятия многопоточного п...
ПВТ - весна 2015 - Лекция 2. POSIX Threads. Основные понятия многопоточного п...Alexey Paznikov
 
ПВТ - осень 2014 - Лекция 7. Многопоточное программирование без блокировок. М...
ПВТ - осень 2014 - Лекция 7. Многопоточное программирование без блокировок. М...ПВТ - осень 2014 - Лекция 7. Многопоточное программирование без блокировок. М...
ПВТ - осень 2014 - Лекция 7. Многопоточное программирование без блокировок. М...Alexey Paznikov
 
Юрий Ефимочев, Компилируемые в реальном времени DSL для С++
Юрий Ефимочев, Компилируемые в реальном времени DSL для С++ Юрий Ефимочев, Компилируемые в реальном времени DSL для С++
Юрий Ефимочев, Компилируемые в реальном времени DSL для С++ Sergey Platonov
 
Дмитрий Кашицын, Троллейбус из буханки: алиасинг и векторизация в LLVM
Дмитрий Кашицын, Троллейбус из буханки: алиасинг и векторизация в LLVMДмитрий Кашицын, Троллейбус из буханки: алиасинг и векторизация в LLVM
Дмитрий Кашицын, Троллейбус из буханки: алиасинг и векторизация в LLVMSergey Platonov
 
Григорий Демченко, Асинхронность и неблокирующая синхронизация
Григорий Демченко, Асинхронность и неблокирующая синхронизацияГригорий Демченко, Асинхронность и неблокирующая синхронизация
Григорий Демченко, Асинхронность и неблокирующая синхронизацияSergey Platonov
 
ПВТ - весна 2015 - Лекция 7. Модель памяти С++. Внеочередное выполнение инстр...
ПВТ - весна 2015 - Лекция 7. Модель памяти С++. Внеочередное выполнение инстр...ПВТ - весна 2015 - Лекция 7. Модель памяти С++. Внеочередное выполнение инстр...
ПВТ - весна 2015 - Лекция 7. Модель памяти С++. Внеочередное выполнение инстр...Alexey Paznikov
 
Полухин Антон, Как делать не надо: C++ велосипедостроение для профессионалов
Полухин Антон, Как делать не надо: C++ велосипедостроение для профессионаловПолухин Антон, Как делать не надо: C++ велосипедостроение для профессионалов
Полухин Антон, Как делать не надо: C++ велосипедостроение для профессионаловSergey Platonov
 
Лекция 8: Многопоточное программирование: Intel Threading Building Blocks
Лекция 8: Многопоточное программирование: Intel Threading Building BlocksЛекция 8: Многопоточное программирование: Intel Threading Building Blocks
Лекция 8: Многопоточное программирование: Intel Threading Building BlocksMikhail Kurnosov
 
Лекция 8. Intel Threading Building Blocks
Лекция 8. Intel Threading Building BlocksЛекция 8. Intel Threading Building Blocks
Лекция 8. Intel Threading Building BlocksMikhail Kurnosov
 
Догнать и перегнать boost::lexical_cast
Догнать и перегнать boost::lexical_castДогнать и перегнать boost::lexical_cast
Догнать и перегнать boost::lexical_castRoman Orlov
 
Павел Сушин «Асинхронное программирование на С++: callbacks, futures, fibers»
Павел Сушин «Асинхронное программирование на С++: callbacks, futures, fibers»Павел Сушин «Асинхронное программирование на С++: callbacks, futures, fibers»
Павел Сушин «Асинхронное программирование на С++: callbacks, futures, fibers»Platonov Sergey
 
Использование юнит-тестов для повышения качества разработки
Использование юнит-тестов для повышения качества разработкиИспользование юнит-тестов для повышения качества разработки
Использование юнит-тестов для повышения качества разработкиvictor-yastrebov
 
Григорий Демченко, Универсальный адаптер
Григорий Демченко, Универсальный адаптерГригорий Демченко, Универсальный адаптер
Григорий Демченко, Универсальный адаптерSergey Platonov
 
Дракон в мешке: от LLVM к C++ и проблемам неопределенного поведения
Дракон в мешке: от LLVM к C++ и проблемам неопределенного поведенияДракон в мешке: от LLVM к C++ и проблемам неопределенного поведения
Дракон в мешке: от LLVM к C++ и проблемам неопределенного поведенияPlatonov Sergey
 
Максим Хижинский Lock-free maps
Максим Хижинский Lock-free mapsМаксим Хижинский Lock-free maps
Максим Хижинский Lock-free mapsPlatonov Sergey
 
Антон Полухин, Немного о Boost
Антон Полухин, Немного о BoostАнтон Полухин, Немного о Boost
Антон Полухин, Немного о BoostSergey Platonov
 
Игорь Кудрин, «Используем неизменяемые данные и создаем качественный код»
Игорь Кудрин, «Используем неизменяемые данные и создаем качественный код»Игорь Кудрин, «Используем неизменяемые данные и создаем качественный код»
Игорь Кудрин, «Используем неизменяемые данные и создаем качественный код»Platonov Sergey
 
Алексей Куканов — Параллелизм в C++: управляйте приложением, а не потоками!
Алексей Куканов — Параллелизм в C++: управляйте приложением, а не потоками!Алексей Куканов — Параллелизм в C++: управляйте приложением, а не потоками!
Алексей Куканов — Параллелизм в C++: управляйте приложением, а не потоками!Yandex
 
разработка серверов и серверных приложений лекция №3
разработка серверов и серверных приложений лекция №3разработка серверов и серверных приложений лекция №3
разработка серверов и серверных приложений лекция №3Eugeniy Tyumentcev
 

What's hot (20)

ПВТ - весна 2015 - Лекция 2. POSIX Threads. Основные понятия многопоточного п...
ПВТ - весна 2015 - Лекция 2. POSIX Threads. Основные понятия многопоточного п...ПВТ - весна 2015 - Лекция 2. POSIX Threads. Основные понятия многопоточного п...
ПВТ - весна 2015 - Лекция 2. POSIX Threads. Основные понятия многопоточного п...
 
ПВТ - осень 2014 - Лекция 7. Многопоточное программирование без блокировок. М...
ПВТ - осень 2014 - Лекция 7. Многопоточное программирование без блокировок. М...ПВТ - осень 2014 - Лекция 7. Многопоточное программирование без блокировок. М...
ПВТ - осень 2014 - Лекция 7. Многопоточное программирование без блокировок. М...
 
Юрий Ефимочев, Компилируемые в реальном времени DSL для С++
Юрий Ефимочев, Компилируемые в реальном времени DSL для С++ Юрий Ефимочев, Компилируемые в реальном времени DSL для С++
Юрий Ефимочев, Компилируемые в реальном времени DSL для С++
 
Дмитрий Кашицын, Троллейбус из буханки: алиасинг и векторизация в LLVM
Дмитрий Кашицын, Троллейбус из буханки: алиасинг и векторизация в LLVMДмитрий Кашицын, Троллейбус из буханки: алиасинг и векторизация в LLVM
Дмитрий Кашицын, Троллейбус из буханки: алиасинг и векторизация в LLVM
 
Parallel STL
Parallel STLParallel STL
Parallel STL
 
Григорий Демченко, Асинхронность и неблокирующая синхронизация
Григорий Демченко, Асинхронность и неблокирующая синхронизацияГригорий Демченко, Асинхронность и неблокирующая синхронизация
Григорий Демченко, Асинхронность и неблокирующая синхронизация
 
ПВТ - весна 2015 - Лекция 7. Модель памяти С++. Внеочередное выполнение инстр...
ПВТ - весна 2015 - Лекция 7. Модель памяти С++. Внеочередное выполнение инстр...ПВТ - весна 2015 - Лекция 7. Модель памяти С++. Внеочередное выполнение инстр...
ПВТ - весна 2015 - Лекция 7. Модель памяти С++. Внеочередное выполнение инстр...
 
Полухин Антон, Как делать не надо: C++ велосипедостроение для профессионалов
Полухин Антон, Как делать не надо: C++ велосипедостроение для профессионаловПолухин Антон, Как делать не надо: C++ велосипедостроение для профессионалов
Полухин Антон, Как делать не надо: C++ велосипедостроение для профессионалов
 
Лекция 8: Многопоточное программирование: Intel Threading Building Blocks
Лекция 8: Многопоточное программирование: Intel Threading Building BlocksЛекция 8: Многопоточное программирование: Intel Threading Building Blocks
Лекция 8: Многопоточное программирование: Intel Threading Building Blocks
 
Лекция 8. Intel Threading Building Blocks
Лекция 8. Intel Threading Building BlocksЛекция 8. Intel Threading Building Blocks
Лекция 8. Intel Threading Building Blocks
 
Догнать и перегнать boost::lexical_cast
Догнать и перегнать boost::lexical_castДогнать и перегнать boost::lexical_cast
Догнать и перегнать boost::lexical_cast
 
Павел Сушин «Асинхронное программирование на С++: callbacks, futures, fibers»
Павел Сушин «Асинхронное программирование на С++: callbacks, futures, fibers»Павел Сушин «Асинхронное программирование на С++: callbacks, futures, fibers»
Павел Сушин «Асинхронное программирование на С++: callbacks, futures, fibers»
 
Использование юнит-тестов для повышения качества разработки
Использование юнит-тестов для повышения качества разработкиИспользование юнит-тестов для повышения качества разработки
Использование юнит-тестов для повышения качества разработки
 
Григорий Демченко, Универсальный адаптер
Григорий Демченко, Универсальный адаптерГригорий Демченко, Универсальный адаптер
Григорий Демченко, Универсальный адаптер
 
Дракон в мешке: от LLVM к C++ и проблемам неопределенного поведения
Дракон в мешке: от LLVM к C++ и проблемам неопределенного поведенияДракон в мешке: от LLVM к C++ и проблемам неопределенного поведения
Дракон в мешке: от LLVM к C++ и проблемам неопределенного поведения
 
Максим Хижинский Lock-free maps
Максим Хижинский Lock-free mapsМаксим Хижинский Lock-free maps
Максим Хижинский Lock-free maps
 
Антон Полухин, Немного о Boost
Антон Полухин, Немного о BoostАнтон Полухин, Немного о Boost
Антон Полухин, Немного о Boost
 
Игорь Кудрин, «Используем неизменяемые данные и создаем качественный код»
Игорь Кудрин, «Используем неизменяемые данные и создаем качественный код»Игорь Кудрин, «Используем неизменяемые данные и создаем качественный код»
Игорь Кудрин, «Используем неизменяемые данные и создаем качественный код»
 
Алексей Куканов — Параллелизм в C++: управляйте приложением, а не потоками!
Алексей Куканов — Параллелизм в C++: управляйте приложением, а не потоками!Алексей Куканов — Параллелизм в C++: управляйте приложением, а не потоками!
Алексей Куканов — Параллелизм в C++: управляйте приложением, а не потоками!
 
разработка серверов и серверных приложений лекция №3
разработка серверов и серверных приложений лекция №3разработка серверов и серверных приложений лекция №3
разработка серверов и серверных приложений лекция №3
 

Viewers also liked

ПВТ - осень 2014 - Лекция 3 - Стандарт POSIX Threads
ПВТ - осень 2014 - Лекция 3 - Стандарт POSIX ThreadsПВТ - осень 2014 - Лекция 3 - Стандарт POSIX Threads
ПВТ - осень 2014 - Лекция 3 - Стандарт POSIX ThreadsAlexey Paznikov
 
ПВТ - осень 2014 - Лекция 2 - Архитектура вычислительных систем с общей памятью
ПВТ - осень 2014 - Лекция 2 - Архитектура вычислительных систем с общей памятьюПВТ - осень 2014 - Лекция 2 - Архитектура вычислительных систем с общей памятью
ПВТ - осень 2014 - Лекция 2 - Архитектура вычислительных систем с общей памятьюAlexey Paznikov
 
ПВТ - осень 2014 - лекция 1а - Описание курса
ПВТ - осень 2014 - лекция 1а - Описание курсаПВТ - осень 2014 - лекция 1а - Описание курса
ПВТ - осень 2014 - лекция 1а - Описание курсаAlexey Paznikov
 
ПВТ - осень 2014 - лекция 1 - Введение в параллельные вычисления
ПВТ - осень 2014 - лекция 1 - Введение в параллельные вычисленияПВТ - осень 2014 - лекция 1 - Введение в параллельные вычисления
ПВТ - осень 2014 - лекция 1 - Введение в параллельные вычисленияAlexey Paznikov
 
Лекция 1. Основные понятия стандарта MPI. Дифференцированные обмены
Лекция 1. Основные понятия стандарта MPI. Дифференцированные обменыЛекция 1. Основные понятия стандарта MPI. Дифференцированные обмены
Лекция 1. Основные понятия стандарта MPI. Дифференцированные обменыAlexey Paznikov
 
Лекция 6. Параллельная сортировка. Алгоритмы комбинаторного поиска. Параллель...
Лекция 6. Параллельная сортировка. Алгоритмы комбинаторного поиска. Параллель...Лекция 6. Параллельная сортировка. Алгоритмы комбинаторного поиска. Параллель...
Лекция 6. Параллельная сортировка. Алгоритмы комбинаторного поиска. Параллель...Alexey Paznikov
 

Viewers also liked (6)

ПВТ - осень 2014 - Лекция 3 - Стандарт POSIX Threads
ПВТ - осень 2014 - Лекция 3 - Стандарт POSIX ThreadsПВТ - осень 2014 - Лекция 3 - Стандарт POSIX Threads
ПВТ - осень 2014 - Лекция 3 - Стандарт POSIX Threads
 
ПВТ - осень 2014 - Лекция 2 - Архитектура вычислительных систем с общей памятью
ПВТ - осень 2014 - Лекция 2 - Архитектура вычислительных систем с общей памятьюПВТ - осень 2014 - Лекция 2 - Архитектура вычислительных систем с общей памятью
ПВТ - осень 2014 - Лекция 2 - Архитектура вычислительных систем с общей памятью
 
ПВТ - осень 2014 - лекция 1а - Описание курса
ПВТ - осень 2014 - лекция 1а - Описание курсаПВТ - осень 2014 - лекция 1а - Описание курса
ПВТ - осень 2014 - лекция 1а - Описание курса
 
ПВТ - осень 2014 - лекция 1 - Введение в параллельные вычисления
ПВТ - осень 2014 - лекция 1 - Введение в параллельные вычисленияПВТ - осень 2014 - лекция 1 - Введение в параллельные вычисления
ПВТ - осень 2014 - лекция 1 - Введение в параллельные вычисления
 
Лекция 1. Основные понятия стандарта MPI. Дифференцированные обмены
Лекция 1. Основные понятия стандарта MPI. Дифференцированные обменыЛекция 1. Основные понятия стандарта MPI. Дифференцированные обмены
Лекция 1. Основные понятия стандарта MPI. Дифференцированные обмены
 
Лекция 6. Параллельная сортировка. Алгоритмы комбинаторного поиска. Параллель...
Лекция 6. Параллельная сортировка. Алгоритмы комбинаторного поиска. Параллель...Лекция 6. Параллельная сортировка. Алгоритмы комбинаторного поиска. Параллель...
Лекция 6. Параллельная сортировка. Алгоритмы комбинаторного поиска. Параллель...
 

Similar to ПВТ - осень 2014 - Лекция 5 - Многопоточное программирование в языке С++. Работа с потоками. Защита данных. Синхронизация. Будущие результаты

Некоторые паттерны реализации полиморфного поведения в C++ – Дмитрий Леванов,...
Некоторые паттерны реализации полиморфного поведения в C++ – Дмитрий Леванов,...Некоторые паттерны реализации полиморфного поведения в C++ – Дмитрий Леванов,...
Некоторые паттерны реализации полиморфного поведения в C++ – Дмитрий Леванов,...Yandex
 
Rust: абстракции и безопасность, совершенно бесплатно
Rust: абстракции и безопасность, совершенно бесплатноRust: абстракции и безопасность, совершенно бесплатно
Rust: абстракции и безопасность, совершенно бесплатноOpen-IT
 
Программирование Linux
Программирование LinuxПрограммирование Linux
Программирование LinuxAnthony Shoumikhin
 
Программирование Linux
Программирование LinuxПрограммирование Linux
Программирование LinuxAnthony Shoumikhin
 
Программирование Linux
Программирование LinuxПрограммирование Linux
Программирование LinuxAnthony Shoumikhin
 
Дмитрий Прокопцев — R-ссылки в С++11
Дмитрий Прокопцев — R-ссылки в С++11Дмитрий Прокопцев — R-ссылки в С++11
Дмитрий Прокопцев — R-ссылки в С++11Yandex
 
Обобщенное программирование в C++ или как сделать свою жизнь проще через стра...
Обобщенное программирование в C++ или как сделать свою жизнь проще через стра...Обобщенное программирование в C++ или как сделать свою жизнь проще через стра...
Обобщенное программирование в C++ или как сделать свою жизнь проще через стра...corehard_by
 
Cpp0x Introduction
Cpp0x IntroductionCpp0x Introduction
Cpp0x IntroductionFedor Vompe
 
Статический и динамический полиморфизм в C++, Дмитрий Леванов
Статический и динамический полиморфизм в C++, Дмитрий ЛевановСтатический и динамический полиморфизм в C++, Дмитрий Леванов
Статический и динамический полиморфизм в C++, Дмитрий ЛевановYandex
 
C++ осень 2012 лекция 11
C++ осень 2012 лекция 11C++ осень 2012 лекция 11
C++ осень 2012 лекция 11Technopark
 
Продолжаем говорить о микрооптимизациях .NET-приложений
Продолжаем говорить о микрооптимизациях .NET-приложенийПродолжаем говорить о микрооптимизациях .NET-приложений
Продолжаем говорить о микрооптимизациях .NET-приложенийAndrey Akinshin
 
Lambdas in java 8
Lambdas in java 8Lambdas in java 8
Lambdas in java 8chashnikov
 
DSLs in Lisp and Clojure
DSLs in Lisp and ClojureDSLs in Lisp and Clojure
DSLs in Lisp and ClojureVasil Remeniuk
 
Шаблоны C++ и базы данных. Сергей Федоров. CoreHard Spring 2019
Шаблоны C++ и базы данных. Сергей Федоров. CoreHard Spring 2019Шаблоны C++ и базы данных. Сергей Федоров. CoreHard Spring 2019
Шаблоны C++ и базы данных. Сергей Федоров. CoreHard Spring 2019corehard_by
 
Многопоточность в браузере. Модель акторов — Константин Крамлих
Многопоточность в браузере. Модель акторов — Константин КрамлихМногопоточность в браузере. Модель акторов — Константин Крамлих
Многопоточность в браузере. Модель акторов — Константин КрамлихYandex
 
Как за час сделать недельную работу
Как за час сделать недельную работуКак за час сделать недельную работу
Как за час сделать недельную работуcorehard_by
 
"Почему язык Lua — это интересно?", Ник Заварицкий, (Mail.ru Group)
"Почему язык Lua — это интересно?", Ник Заварицкий, (Mail.ru Group)"Почему язык Lua — это интересно?", Ник Заварицкий, (Mail.ru Group)
"Почему язык Lua — это интересно?", Ник Заварицкий, (Mail.ru Group)Badoo Development
 
20130429 dynamic c_c++_program_analysis-alexey_samsonov
20130429 dynamic c_c++_program_analysis-alexey_samsonov20130429 dynamic c_c++_program_analysis-alexey_samsonov
20130429 dynamic c_c++_program_analysis-alexey_samsonovComputer Science Club
 
Лекция 4: Стек. Очередь
Лекция 4: Стек. ОчередьЛекция 4: Стек. Очередь
Лекция 4: Стек. ОчередьMikhail Kurnosov
 
Борис Сазонов, RAII потоки и CancellationToken в C++
Борис Сазонов, RAII потоки и CancellationToken в C++Борис Сазонов, RAII потоки и CancellationToken в C++
Борис Сазонов, RAII потоки и CancellationToken в C++Sergey Platonov
 

Similar to ПВТ - осень 2014 - Лекция 5 - Многопоточное программирование в языке С++. Работа с потоками. Защита данных. Синхронизация. Будущие результаты (20)

Некоторые паттерны реализации полиморфного поведения в C++ – Дмитрий Леванов,...
Некоторые паттерны реализации полиморфного поведения в C++ – Дмитрий Леванов,...Некоторые паттерны реализации полиморфного поведения в C++ – Дмитрий Леванов,...
Некоторые паттерны реализации полиморфного поведения в C++ – Дмитрий Леванов,...
 
Rust: абстракции и безопасность, совершенно бесплатно
Rust: абстракции и безопасность, совершенно бесплатноRust: абстракции и безопасность, совершенно бесплатно
Rust: абстракции и безопасность, совершенно бесплатно
 
Программирование Linux
Программирование LinuxПрограммирование Linux
Программирование Linux
 
Программирование Linux
Программирование LinuxПрограммирование Linux
Программирование Linux
 
Программирование Linux
Программирование LinuxПрограммирование Linux
Программирование Linux
 
Дмитрий Прокопцев — R-ссылки в С++11
Дмитрий Прокопцев — R-ссылки в С++11Дмитрий Прокопцев — R-ссылки в С++11
Дмитрий Прокопцев — R-ссылки в С++11
 
Обобщенное программирование в C++ или как сделать свою жизнь проще через стра...
Обобщенное программирование в C++ или как сделать свою жизнь проще через стра...Обобщенное программирование в C++ или как сделать свою жизнь проще через стра...
Обобщенное программирование в C++ или как сделать свою жизнь проще через стра...
 
Cpp0x Introduction
Cpp0x IntroductionCpp0x Introduction
Cpp0x Introduction
 
Статический и динамический полиморфизм в C++, Дмитрий Леванов
Статический и динамический полиморфизм в C++, Дмитрий ЛевановСтатический и динамический полиморфизм в C++, Дмитрий Леванов
Статический и динамический полиморфизм в C++, Дмитрий Леванов
 
C++ осень 2012 лекция 11
C++ осень 2012 лекция 11C++ осень 2012 лекция 11
C++ осень 2012 лекция 11
 
Продолжаем говорить о микрооптимизациях .NET-приложений
Продолжаем говорить о микрооптимизациях .NET-приложенийПродолжаем говорить о микрооптимизациях .NET-приложений
Продолжаем говорить о микрооптимизациях .NET-приложений
 
Lambdas in java 8
Lambdas in java 8Lambdas in java 8
Lambdas in java 8
 
DSLs in Lisp and Clojure
DSLs in Lisp and ClojureDSLs in Lisp and Clojure
DSLs in Lisp and Clojure
 
Шаблоны C++ и базы данных. Сергей Федоров. CoreHard Spring 2019
Шаблоны C++ и базы данных. Сергей Федоров. CoreHard Spring 2019Шаблоны C++ и базы данных. Сергей Федоров. CoreHard Spring 2019
Шаблоны C++ и базы данных. Сергей Федоров. CoreHard Spring 2019
 
Многопоточность в браузере. Модель акторов — Константин Крамлих
Многопоточность в браузере. Модель акторов — Константин КрамлихМногопоточность в браузере. Модель акторов — Константин Крамлих
Многопоточность в браузере. Модель акторов — Константин Крамлих
 
Как за час сделать недельную работу
Как за час сделать недельную работуКак за час сделать недельную работу
Как за час сделать недельную работу
 
"Почему язык Lua — это интересно?", Ник Заварицкий, (Mail.ru Group)
"Почему язык Lua — это интересно?", Ник Заварицкий, (Mail.ru Group)"Почему язык Lua — это интересно?", Ник Заварицкий, (Mail.ru Group)
"Почему язык Lua — это интересно?", Ник Заварицкий, (Mail.ru Group)
 
20130429 dynamic c_c++_program_analysis-alexey_samsonov
20130429 dynamic c_c++_program_analysis-alexey_samsonov20130429 dynamic c_c++_program_analysis-alexey_samsonov
20130429 dynamic c_c++_program_analysis-alexey_samsonov
 
Лекция 4: Стек. Очередь
Лекция 4: Стек. ОчередьЛекция 4: Стек. Очередь
Лекция 4: Стек. Очередь
 
Борис Сазонов, RAII потоки и CancellationToken в C++
Борис Сазонов, RAII потоки и CancellationToken в C++Борис Сазонов, RAII потоки и CancellationToken в C++
Борис Сазонов, RAII потоки и CancellationToken в C++
 

More from Alexey Paznikov

Лекция 5. Метод конечных разностей (параллельные алгоритмы в стандарте MPI)
Лекция 5. Метод конечных разностей (параллельные алгоритмы в стандарте MPI)Лекция 5. Метод конечных разностей (параллельные алгоритмы в стандарте MPI)
Лекция 5. Метод конечных разностей (параллельные алгоритмы в стандарте MPI)Alexey Paznikov
 
Лекция 4. Производные типы данных в стандарте MPI
Лекция 4. Производные типы данных в стандарте MPIЛекция 4. Производные типы данных в стандарте MPI
Лекция 4. Производные типы данных в стандарте MPIAlexey Paznikov
 
Лекция 3. Виртуальные топологии в MPI. Параллельные алгоритмы в стандарте MPI...
Лекция 3. Виртуальные топологии в MPI. Параллельные алгоритмы в стандарте MPI...Лекция 3. Виртуальные топологии в MPI. Параллельные алгоритмы в стандарте MPI...
Лекция 3. Виртуальные топологии в MPI. Параллельные алгоритмы в стандарте MPI...Alexey Paznikov
 
Лекция 2. Коллективные операции в MPI. Параллельные алгоритмы случайного блуж...
Лекция 2. Коллективные операции в MPI. Параллельные алгоритмы случайного блуж...Лекция 2. Коллективные операции в MPI. Параллельные алгоритмы случайного блуж...
Лекция 2. Коллективные операции в MPI. Параллельные алгоритмы случайного блуж...Alexey Paznikov
 
ПВТ - весна 2015 - Лекция 1. Актуальность параллельных вычислений. Анализ пар...
ПВТ - весна 2015 - Лекция 1. Актуальность параллельных вычислений. Анализ пар...ПВТ - весна 2015 - Лекция 1. Актуальность параллельных вычислений. Анализ пар...
ПВТ - весна 2015 - Лекция 1. Актуальность параллельных вычислений. Анализ пар...Alexey Paznikov
 
ПВТ - весна 2015 - Лекция 0. Описание курса
ПВТ - весна 2015 - Лекция 0. Описание курсаПВТ - весна 2015 - Лекция 0. Описание курса
ПВТ - весна 2015 - Лекция 0. Описание курсаAlexey Paznikov
 
Анализ эффективности выполнения алгоритма параллельной редукции в языке Cray ...
Анализ эффективности выполнения алгоритма параллельной редукции в языке Cray ...Анализ эффективности выполнения алгоритма параллельной редукции в языке Cray ...
Анализ эффективности выполнения алгоритма параллельной редукции в языке Cray ...Alexey Paznikov
 
ТФРВС - весна 2014 - лекция 11
ТФРВС - весна 2014 - лекция 11ТФРВС - весна 2014 - лекция 11
ТФРВС - весна 2014 - лекция 11Alexey Paznikov
 
ТФРВС - весна 2014 - лекция 10
ТФРВС - весна 2014 - лекция 10ТФРВС - весна 2014 - лекция 10
ТФРВС - весна 2014 - лекция 10Alexey Paznikov
 
ТФРВС - весна 2014 - лекция 9
 ТФРВС - весна 2014 - лекция 9 ТФРВС - весна 2014 - лекция 9
ТФРВС - весна 2014 - лекция 9Alexey Paznikov
 
ТФРВС - весна 2014 - лекция 8
ТФРВС - весна 2014 - лекция 8ТФРВС - весна 2014 - лекция 8
ТФРВС - весна 2014 - лекция 8Alexey Paznikov
 
ТФРВС - весна 2014 - лекция 7
ТФРВС - весна 2014 - лекция 7ТФРВС - весна 2014 - лекция 7
ТФРВС - весна 2014 - лекция 7Alexey Paznikov
 
ТФРВС - весна 2014 - лекция 6
ТФРВС - весна 2014 - лекция 6ТФРВС - весна 2014 - лекция 6
ТФРВС - весна 2014 - лекция 6Alexey Paznikov
 

More from Alexey Paznikov (13)

Лекция 5. Метод конечных разностей (параллельные алгоритмы в стандарте MPI)
Лекция 5. Метод конечных разностей (параллельные алгоритмы в стандарте MPI)Лекция 5. Метод конечных разностей (параллельные алгоритмы в стандарте MPI)
Лекция 5. Метод конечных разностей (параллельные алгоритмы в стандарте MPI)
 
Лекция 4. Производные типы данных в стандарте MPI
Лекция 4. Производные типы данных в стандарте MPIЛекция 4. Производные типы данных в стандарте MPI
Лекция 4. Производные типы данных в стандарте MPI
 
Лекция 3. Виртуальные топологии в MPI. Параллельные алгоритмы в стандарте MPI...
Лекция 3. Виртуальные топологии в MPI. Параллельные алгоритмы в стандарте MPI...Лекция 3. Виртуальные топологии в MPI. Параллельные алгоритмы в стандарте MPI...
Лекция 3. Виртуальные топологии в MPI. Параллельные алгоритмы в стандарте MPI...
 
Лекция 2. Коллективные операции в MPI. Параллельные алгоритмы случайного блуж...
Лекция 2. Коллективные операции в MPI. Параллельные алгоритмы случайного блуж...Лекция 2. Коллективные операции в MPI. Параллельные алгоритмы случайного блуж...
Лекция 2. Коллективные операции в MPI. Параллельные алгоритмы случайного блуж...
 
ПВТ - весна 2015 - Лекция 1. Актуальность параллельных вычислений. Анализ пар...
ПВТ - весна 2015 - Лекция 1. Актуальность параллельных вычислений. Анализ пар...ПВТ - весна 2015 - Лекция 1. Актуальность параллельных вычислений. Анализ пар...
ПВТ - весна 2015 - Лекция 1. Актуальность параллельных вычислений. Анализ пар...
 
ПВТ - весна 2015 - Лекция 0. Описание курса
ПВТ - весна 2015 - Лекция 0. Описание курсаПВТ - весна 2015 - Лекция 0. Описание курса
ПВТ - весна 2015 - Лекция 0. Описание курса
 
Анализ эффективности выполнения алгоритма параллельной редукции в языке Cray ...
Анализ эффективности выполнения алгоритма параллельной редукции в языке Cray ...Анализ эффективности выполнения алгоритма параллельной редукции в языке Cray ...
Анализ эффективности выполнения алгоритма параллельной редукции в языке Cray ...
 
ТФРВС - весна 2014 - лекция 11
ТФРВС - весна 2014 - лекция 11ТФРВС - весна 2014 - лекция 11
ТФРВС - весна 2014 - лекция 11
 
ТФРВС - весна 2014 - лекция 10
ТФРВС - весна 2014 - лекция 10ТФРВС - весна 2014 - лекция 10
ТФРВС - весна 2014 - лекция 10
 
ТФРВС - весна 2014 - лекция 9
 ТФРВС - весна 2014 - лекция 9 ТФРВС - весна 2014 - лекция 9
ТФРВС - весна 2014 - лекция 9
 
ТФРВС - весна 2014 - лекция 8
ТФРВС - весна 2014 - лекция 8ТФРВС - весна 2014 - лекция 8
ТФРВС - весна 2014 - лекция 8
 
ТФРВС - весна 2014 - лекция 7
ТФРВС - весна 2014 - лекция 7ТФРВС - весна 2014 - лекция 7
ТФРВС - весна 2014 - лекция 7
 
ТФРВС - весна 2014 - лекция 6
ТФРВС - весна 2014 - лекция 6ТФРВС - весна 2014 - лекция 6
ТФРВС - весна 2014 - лекция 6
 

ПВТ - осень 2014 - Лекция 5 - Многопоточное программирование в языке С++. Работа с потоками. Защита данных. Синхронизация. Будущие результаты

  • 2. ˜˶˻̇˹̐˝˾˿˴˿̀˿̃˿̈˾˿˶ ̀́˿˴́˱˽˽˹́˿˳˱˾˹˶˳̐˸̌˻˶ˢ ˡ˱˲˿̃˱̂̀˿̃˿˻˱˽˹˘˱̊˹̃˱˵˱˾˾̌̆ ˢ˹˾̆́˿˾˹˸˱̇˹̐˒̄˵̄̊˹˶́˶˸̄˼̍̃˱̃̌ ˠ˱˸˾˹˻˿˳ˑ˼˶˻̂˶˺ˑ˼˶˻̂˱˾˵́˿˳˹̈ ˛˱̅˶˵́˱˳̌̈˹̂˼˹̃˶˼̍˾̌̆̂˹̂̃˶˽ˢ˹˲˔ˤˣ˙ ˢ˱˺̃˻̄́̂˱KWWSFSFWVLEVXWLVUXaDSD]QLNRYWHDFKLQJ ˓˿̀́˿̂̌KWWSVSLD]]DFRPVLEVXWLVUXIDOOSFWKRPH
  • 4. ˟˵˹˳˾̌˺˾˿˳̌˺̀˱́˱˼˼˶˼̍˾̌˺@˽˹́ #include iostream #include thread void hello() { // функция, которая реализует поток std::cout hello brave new world!n; } int main() { std::thread t(hello); // создаём поток t.join(); // дожидаемся завершения } 3
  • 5. ˘˱̀̄̂˻̀˿̃˿˻˱ #include iostream #include thread void hello() { class thread_class { // класс с перегруженным оператором() public: void operator()() const { hello(); bye(); } } std::cout hello brave new world!n; } int main() { std::thread t(hello); t.join(); } 4
  • 6. ˘˱̀̄̂˻̀˿̃˿˻˱ class thread_class { public: void operator()() const { hello(); bye(); } } std::thread thr((thread_class())); // зачем ()? std::thread thr{thread_class} // так лучше! std::thread thr([](){ std::cout hello worldn; }); thr.join(); Most vexing parse 5
  • 7. ˟̃̂˿˶˵˹˾̒˾˾̌˺̀˿̃˿˻ struct func { int i; func(int _i): i{_i} {} void operator()() { std::cout i std::endl;// доступ к висячей сслыке } }; int main(int argc, const char *argv[]) { int local = 100; func myfunc(local); std::thread thr(myfunc); thr.detach(); // отсоединяем поток... } // поток ещё работает! 6
  • 8. ˟˷˹˵˱˾˹˶˸˱˳˶́̉˶˾˹̐̀˿̃˿˻˱˳̂˼̄̈˱˶˹̂˻˼̏̈˶˾˹̐ std::thread thr(myfunc); try { std::cout hello; // ... throw error; } catch (...) { thr.join(); // не забыть дождаться завершения std::cout exception catchedn; return 1; } thr.join(); 7
  • 9. ˟˷˹˵˱˾˹˶˸˱˳˶́̉˶˾˹̐̀˿̃˿˻˱˳̂˼̄̈˱˶˹̂˻˼̏̈˶˾˹̐ class thread_guard { std::thread t; public: explicit thread_guard(std::thread _t): t{_t} {} ~thread_guard() { if (t.joinable()) { t.join(); } } thread_guard(thread_guard const) = delete; thread_guard operator=(thread_guard const) = delete; }; void foo() { int local; std::thread t{func(local)}; thread_guard g(t); } // t.join() 8
  • 10. ˘˱̀̄̂˻˾˶̂˻˿˼̍˻˹̆̀˿̃˿˻˿˳˹˿˷˹˵˱˾˹˶˸˱˳˶́̉˶˾˹̐ int main() { std::vectorstd::thread threads; for (auto i = 0; i 10; i++) { threads.push_back(std::thread([i](){ std::cout i n; })); } for_each(threads.begin(), threads.end(), std::mem_fn(std::thread::join)); } 9
  • 11. ˘˱̀̄̂˻˾˶̂˻˿˼̍˻˹̆̀˿̃˿˻˿˳˹˹˵˶˾̃˹̅˹˻˱̃˿́̌̀˿̃˿˻˿˳ std::vectorstd::thread threads; std::mapstd::thread::id, int table; for (auto i = 0; i 10; i++) { threads.push_back(std::thread([i](){ std::this_thread::sleep_for( std::chrono::milliseconds(100 * i)); std::cout i n; })); table.insert(std::make_pair(threads.back().get_id(), i % 2)); } std::cout value of 5: table[threads[5].get_id()] std::endl; std::cout value of 6: table[threads[6].get_id()] std::endl; for_each(threads.begin(), threads.end(), std::mem_fn(std::thread::join)); 10
  • 12. ˠ˶́˶˵˱̈˱˱́˴̄˽˶˾̃˿˳̅̄˾˻̇˹˹̀˿̃˿˻˱ void func(int i, std::string const s1, std::string const s2) { std::cout s1 s2 std::endl; } int main() { std::thread t(func, 2014, hello, world); t.join(); } 11
  • 13. ˠ˶́˶˵˱̈˱˱́˴̄˽˶˾̃˿˳̅̄˾˻̇˹˹̀˿̃˿˻˱ void func(int i, std::string const s1, std::string const s2) { std::cout s1 s2 std::endl; } int main() { char buf[] = hello; std::thread t(func, 2014, buf, world); t.detach(); } 12
  • 14. ˠ˶́˶˵˱̈˱˱́˴̄˽˶˾̃˿˳̅̄˾˻̇˹˹̀˿̃˿˻˱ void func(int i, std::string const s1, std::string const s2) { std::cout s1 s2 std::endl; } int main() { char buf[] = hello; // автоматическая переменная std::thread t(func, 2014, buf, world); t.detach(); } // переменной foo нет, // а поток продолжает выполняться Использование висячего указателя 13
  • 15. ˠ˶́˶˵˱̈˱˱́˴̄˽˶˾̃˿˳̅̄˾˻̇˹˹̀˿̃˿˻˱ void func(int i, std::string const s1, std::string const s2) { std::cout s1 s2 std::endl; } int main() { char buf[] = hello; std::thread t(func, 2014, std::string(buf), world); t.detach(); } Явное преобразование позволяет избежать висячего указателя 14
  • 16. ˠ˶́˶˵˱̈˱˱́˴̄˽˶˾̃˿˳̅̄˾˻̇˹˹̀˿̃˿˻˱ void func(std::string s_arg) { s_arg = hello parallel world; } int main() { std::string s{hello world}; std::thread t(func, s); t.join(); std::cout s std::endl; // результат операции? return 0; } 15
  • 17. ˠ˶́˶˵˱̈˱˱́˴̄˽˶˾̃˿˳̅̄˾˻̇˹˹̀˿̃˿˻˱̀˿̂̂̌˼˻˶ void func(std::string s_arg) { s_arg = hello parallel world; } int main() { std::string s{hello world}; std::thread t(func, std::ref(s)); // передача по ссылке! t.join(); std::cout s std::endl; // hello parallel world return 0; } 16
  • 18. ˠ˶́˶˵˱̈˱̄̀́˱˳˼˶˾˹̐̀˿̃˿˻˿˽ struct bulky { // некий массивный объект std::string name; void print() { std::cout I'm name std::endl; } }; void func(std::unique_ptrbulky obj) { obj-print(); } int main() { std::unique_ptrbulky ptr{new bulky{Ivan}}; std::thread t(func, ptr); t.join(); } 17
  • 19. ˠ˶́˶˵˱̈˱˱́˴̄˽˶˾̃˿˳ struct bulky { // некий массивный объект std::string name; void print() { std::cout I'm name std::endl; } }; void func(std::unique_ptrbulky obj) { obj-print(); } int main() { std::unique_ptrbulky ptr{new bulky{Ivan}}; std::thread t(func, std::move(ptr)); t.join(); } 18
  • 20. ˠ˶́˶˵˱̈˱̄̀́˱˳˼˶˾˹̐̀˿̃˿˻˿˽ void foo() { } void bar() { } int main() { std::thread t1(foo); std::thread t2 = std::move(t1); // перемещение t1 = std::thread(bar); std::thread t3; t1 = std::move(t3); // ошибка! t3 = std::move(t2); t3 = std::move(t2); // ошибка! t1.join(); t2.join(); // ошибка! t3.join(); } 19
  • 21. ˠ˶́˶˵˱̈˱̄̀́˱˳˼˶˾˹̐̀˿̃˿˻˿˽ std::thread foo() { std::thread thr([](){ std::cout threadn; }); return thr; // перемещение } void bar(std::thread thr) { thr.join(); } int main() { std::thread thr = foo(); bar(std::move(thr)); // перемещение return 0; } 20
  • 22. ˠ˶́˶˵˱̈˱̄̀́˱˳˼˶˾˹̐̀˿̃˿˻˿˽ class scoped_thread { std::thread t; public: explicit scoped_thread(std::thread _t): t{std::move(_t)} { if (!t.joinable()) throw std::logic_error(no thread); } ~scoped_thread() { t.join(); } scoped_thread(thread_guard const) = delete; scoped_thread operator=(thread_guard const) = delete; }; void foo() { int local; scoped_thread t{std::thread(func((local))}; } 21
  • 23. ˠ˱́˱˼˼˶˼̍˾˱̐˳˶́̂˹̐˱˼˴˿́˹̃˽˱DFFXPXODWH 38 38 38 accumulate_block accumulate_block accumulate_block Поток 1 Поток 2 Поток num_threads 22
  • 24. ˠ˱́˱˼˼˶˼̍˾˱̐˳˶́̂˹̐˱˼˴˿́˹̃˽˱DFFXPXODWH const int SIZE = 10; templatetypename Iterator, typename T, typename BinOperation struct accumulate_block { // каждый поток рассчитывает свой блок void operator()(Iterator first, Iterator last, T result, BinOperation op) { result = std::accumulate(first, last, result, op); } }; 23
  • 25. ˠ˱́˱˼˼˶˼̍˾˱̐˳˶́̂˹̐˱˼˴˿́˹̃˽˱DFFXPXODWH templatetypename Iterator, typename T, typename BinOp T parallel_accumulate(Iterator first, Iterator last, T init, BinOp op) { unsigned long const length = std::distance(first, last); if (!length) return init; макс. число unsigned long const min_per_thread = 2; потоков unsigned long const max_threads = (length + min_per_thread - 1) / min_per_thread; unsigned long const hardware_threads = std::thread::hardware_concurrency(); unsigned long const num_threads = аппаратный предел числа потоков (ядер) std::min(hardware_threads != 0 ? hardware_threads : 2, max_threads); число потоков размер блока unsigned long const block_size = length / num_threads; std::vectorT results(num_threads); std::vectorstd::thread threads(num_threads - 1); 24
  • 26. ˠ˱́˱˼˼˶˼̍˾˱̐˳˶́̂˹̐˱˼˴˿́˹̃˽˱DFFXPXODWH Iterator block_start = first; for (unsigned long i = 0; i num_threads - 1; i++) { Iterator block_end = block_start; std::advance(block_end, block_size); // конец блока // каждый поток рассчитывает свой блок threads[i] = std::thread( accumulate_blockIterator, T, BinOperation(), block_start, block_end, std::ref(results[i]), op); block_start = block_end; последний блок (+остаток) } accumulate_blockIterator, T, BinOperation() (block_start, last, results[num_threads - 1], op); std::for_each(threads.begin(), threads.end(), std::mem_fn(std::thread::join)); return std::accumulate(results.begin(), results.end(), init, op); } 25
  • 27. ˠ˱́˱˼˼˶˼̍˾˱̐˳˶́̂˹̐˱˼˴˿́˹̃˽˱DFFXPXODWH int main() { std::vectorint vec(SIZE); for (auto x: vec) x = rand() % 10; std::cout acc: parallel_accumulate(vec.begin(), vec.end(), 0, std::plusint()) std::endl; return 0; } 26
  • 29. ˝̍̏̃˶˻̂̌˳ˢ std::listint mylist; std::mutex lock; void add(int elem) { std::lock_guardstd::mutex guard(lock); mylist.push_back(elem); } bool find(int elem) { std::lock_guardstd::mutex guard(lock); return std::find(mylist.begin(), mylist.end(), elem) != mylist.end(); } 28
  • 30. ˝̍̏̃˶˻̂̌˳ˢ std::listint mylist; std::mutex lock; void add(int elem) { std::lock_guardstd::mutex guard(lock); if (elem 0) throw error; mylist.push_back(elem); } bool find(int elem) { std::lock_guardstd::mutex guard(lock); if (mylist.size() == 0) return false; return std::find(mylist.begin(), mylist.end(), elem) != mylist.end(); } 29
  • 31. ˝̍̏̃˶˻̂̌˳ˢ class wrapper { private: data_t data; // защищаемые данные std::mutex mut; public: templatetypename Function void proc_data(Function func) { std::lock_guardstd::mutex lock(mut); func(data); } }; data_t *unprotected; // внешний указатель void unsafe_func(data_t protected) { unprotected = protected; Любой код, имеющий доступ к указателю или ссылке, может делать с ним всё, что угодно, не захватывая мьютекс. } wrapper obj; obj.proc_data(unsafe_func); unprotected-do_something(); // незащищённый доступ к data 30
  • 32. ˝̍̏̃˶˻̂̌˳ˢ class wrapper { private: data_t data; // защищаемые данные std::mutex mut; public: templatetypename Function void proc_data(Function func) { Нельзя передавать указатели и ссылки на защищённые данные за пределы области видимости блокировки никаким образом. std::lock_guardstd::mutex lock(mut); func(data); } }; data_t *unprotected; // внешний указатель void unsafe_func(data_t protected) { unprotected = protected; Любой код, имеющий доступ к указателю или ссылке, может делать с ним всё, что угодно, не захватывая мьютекс. } wrapper obj; obj.proc_data(unsafe_func); unprotected-do_something(); // незащищённый доступ к data 31
  • 33. ˑ˵˱̀̃˱̇˹̐˹˾̃˶́̅˶˺̂˿˳˻˽˾˿˴˿̀˿̃˿̈˾˿̂̃˹ template ... class stack { public: // ... bool empty() const; size_t size() const; T top(); T const top() const; void push(T const); void push(T); void pop(); void swap(stack); }; 32
  • 34. ˑ˵˱̀̃˱̇˹̐˹˾̃˶́̅˶˺̂˿˳˻˽˾˿˴˿̀˿̃˿̈˾˿̂̃˹ template ... class stack { public: // ... bool empty() const; size_t size() const; T top(); T const top() const; void push(T const); void push(T); void pop(); void swap(stack); }; 33
  • 35. ˑ˵˱̀̃˱̇˹̐˹˾̃˶́̅˶˺̂˿˳˻˽˾˿˴˿̀˿̃˿̈˾˿̂̃˹ template ... class stack { public: // ... bool empty() const; size_t size() const; T top(); T const top() const; void push(T const); void push(T); void pop(); void swap(stack); }; stackint s; if (!s.empty()) { int const value = s.top(); s.pop(); // ... } некорректный результат как решить? 34
  • 36. ˑ˵˱̀̃˱̇˹̐˹˾̃˶́̅˶˺̂˿˳˻˽˾˿˴˿̀˿̃˿̈˾˿̂̃˹ 1. Передавать ссылку в функцию pop std::vectorint result; mystack.pop(result); 2. Потребовать наличия копирующего или перемещающего конструктора, не возбуждающего исключений (доказано, что можно объединить pop и top, но это можно сделать только если конструкторы не вызывают исключений) 3. Возвращать указатель на вытолкнутый элемент std::shared_ptrT pop() 4. Одновременно 1 и один из вариантов 2 или 3 35
  • 37. ˠ˿̃˿˻˿˲˶˸˿̀˱̂˾̌˺̂̃˶˻ templatetypename T class safe_stack { private: std::stackT data; mutable std::mutex m; public: safe_stack(); safe_stack(const safe_stack); // стек нельзя присваивать safe_stack operator=(const safe_stack) = delete; void push(T new_value); std::shared_ptrT pop(); void pop(T value); bool empty() const; // swap отсутствует // -- интерфейс предельно упрощён -- }; 36
  • 38. ˠ˿̃˿˻˿˲˶˸˿̀˱̂˾̌˺̂̃˶˻ safe_stack(const safe_stack rhs) { std::lock_guardstd::mutex lock(rhs.m); data = rhs.data; } std::shared_ptrT pop() { std::lock_guardstd::mutex lock(m); if (data.empty()) throw empty_stack(); // выделяем память под возвращаемое значение std::shared_ptrT const res(std::make_sharedT(data.top())); data.pop(); return res; } void pop(T value) { std::lock_guardstd::mutex lock(m); if (data.empty()) throw empty_stack(); value = data.top(); data.pop(); } 37
  • 39. ˕˶˵˼˿˻˹˸˱̆˳˱̃˾˶̂˻˿˼̍˻˹̆˽̍̏̃˶˻̂˿˳ class Widget { private: data obj; std::mutex m; public: Widget(data const d): data(d) {} friend void swap(Widget lhs, Widget rhs) { if (lhs == rhs) return; std::lock(lhs.m, rhs.m); // захыватываем мьютекс // adopt_lock: lock_a и lock_b начинают владеть // захваченной блокировкой std::lock_guardstd::mutex lock_a(lhs.m, std::adopt_lock); std::lock_guardstd::mutex lock_b(rhs.m, std::adopt_lock); swap(lhs.some_detail, rhs.some_detail); } }; lock - “всё или ничего” 38
  • 40. ˕˶˵˼˿˻˹˹˶́˱́̆˹̈˶̂˻˹˶˽̍̏̃˶˻̂̌ hierarchical_mutex(100) hierarchical_mutex(50) hierarchical_mutex(30) 1 - 2 - 3 1 2 3 2 - 3 3 - 1 3 - 2 порядок запирания 39
  • 41. ˕˶˵˼˿˻˹˹˶́˱́̆˹̈˶̂˻˹˶˽̍̏̃˶˻̂̌ 1 2 3 hierarchical_mutex(100) hierarchical_mutex(50) hierarchical_mutex(30) 1 - 2 - 3 2 - 3 3 - 1 3 - 2 порядок запирания 40
  • 42. ˕˶˵˼˿˻˹˹˶́˱́̆˹̈˶̂˻˹˶˽̍̏̃˶˻̂̌̀́˹˽˶́ hier_mutex high_level_mut(10000); hier_mutex low_level_mut(5000); int low_level_func() { // низкоуровневая блокировка std::lock_guardhier_mutex lock(low_level_mut); do_low_level_stuff(); } void high_level_func() { // высокоуровневая блокировка std::lock_guardhier_mut lock(high_level_mut); do_high_level_stuff(low_level_func()); // корректный } // порядок void thread_a() { high_level_func(); // всё ок } hier_mutex lowest_level_mut(100); void thread_b() { // некорректный порядок блокировки! std::lock_guardhier_mut lock(lowest_level_mut); high_level_func(); // вызов недопустим } 41
  • 43. ˙˶́˱́̆˹̈˶̂˻˹˶˽̍̏̃˶˻̂̌˳˿˸˽˿˷˾˱̐́˶˱˼˹˸˱̇˹̐ class hier_mutex { // hierarchical mutex private: std::mutex internal_mut; unsigned long const hier_val; // текущий уровень unsigned prev_hier_val; // предыдущий уровень // уровень иерархии текущего потока static thread_local unsigned long this_thread_hier_val; void check_for_hier_violation() { if (this_thread_hier_val = hier_val) { throw std::logic_error(mutex hierarchy violated); } } // обновить текущий уровень иерархии потока void update_hier_val() { prev_hier_val = this_thread_hier_val; this_thread_hier_val = hier_val; } 42
  • 44. ˙˶́˱́̆˹̈˶̂˻˹˶˽̍̏̃˶˻̂̌˳˿˸˽˿˷˾˱̐́˶˱˼˹˸˱̇˹̐ public: explicit hier_mutex(unsigned long value): hier_val(value), prev_hier_value(0) {} void lock() { check_for_hier_violation(); internal_mutex.lock(); update_hier_val(); } void unlock() { this_thrad_hier_val = prev_hier_val; internal_mutex.unlock(); } void trylock() { // ... } thread_local unsigned long hier_mutex::this_thread_hier_val(ULONG_MAX); 43
  • 45. ˒˼˿˻˹́˿˳˻˱̂̀˿˽˿̊̍̏VWGXQLTXHBORFN class Widget { int val; std::mutex m; int getval() const { return val; } }; bool Cmp(Widget lhs, Widget rhs) { // не захватываем пока мьютексы std::unique_lockstd::mutex lock1(lhs.m,std::defer_lock); std::unique_lockstd::mutex lock2(rhs.m,std::defer_lock); // а вот сейчас захватываем, причём без дедлоков std::lock(lock1, lock2); return lhs.getval() rhs.getval() ? true : false; } 44
  • 46. ˒˼˿˻˹́˿˳˻˱̂̀˿˽˿̊̍̏VWGXQLTXHBORFN class Widget { int val; std::mutex m; int getval() const { std::lock_guardstd::mutex lock(m); return val; } }; bool Cmp(Widget lhs, Widget rhs) { // обе операции совершаются под защитой мьютекса int const lhs_val = lhs.getval(); int const rhs_val = rhs.getval(); std::lock(lock1, lock2); return lhs_val rhs_val ? true : false; } Минимизация гранулярности блокировки! 45
  • 47. ˒˼˿˻˹́˿˳˻˱̂̀˿˽˿̊̍̏VWGXQLTXHBORFN void pop_and_process() { std::unique_lockstd::mutex lock(mut); Widget data = queue.pop(); // получить элемент данных lock.unlock(); // освободить мьютекс super_widget result = process(data); // обработать данные lock.lock(); // опять захватить мьютекс output_result(data, result); // вывести результат } Минимизация блокировок! ▪ блокировать данные, а не операции ▪ удерживать мьютекс столько, сколько необходимо ▫ тяжёлые операции (захват другого мьютекса, ввод/вывод и т.д.) - вне текущей критической секции 46
  • 48. ˟˵˾˿˻́˱̃˾̌˺˳̌˸˿˳˹˿̃˼˿˷˶˾˾˱̐˹˾˹̇˹˱˼˹˸˱̇˹̐ class NetFacility { private: connect_handle connection; bool connection_flag; void open_connection() { connection = connect_manager.open(); } public: NetFacility(connect_info _info): {} void send_data(data_packet const d) { // отложенная инициализация if (connection_flag == false) connection = open_connection(); connection.send(data); } void recv_data() { /* ... */ } } А если несколько потоков? 47
  • 49. ˟˵˾˿˻́˱̃˾̌˺˳̌˸˿˳˹˿̃˼˿˷˶˾˾˱̐˹˾˹̇˹˱˼˹˸˱̇˹̐ class NetFacility { private: connect_handle connection; bool connection_flag; std::mutex mut; void open_connection() { connection = connect_manager.open(); } public: NetFacility(connect_info _info): {} void send_data(data_packet const d) { std::unique_lockstd::mutex lock(mut); if (connection_flag == false) // только инициализация требует защиты! connection = open_connection(); mut.unlock(); connection.send(data); } void recv_data() { /* ... */ } }; Защищать только инициализацию 48
  • 50. ˟˵˾˿˻́˱̃˾̌˺˳̌˸˿˳˹˿̃˼˿˷˶˾˾˱̐˹˾˹̇˹˱˼˹˸˱̇˹̐ class NetFacility { private: connect_handle connection; bool connection_flag; std::mutex mut; void open_connection() { connection = connect_manager.open(); } public: NetFacility(connect_info _info): {} void send_data(data_packet const d) { if (connection_flag == false) { // гонка! std::lock_guardstd::mutex lock(mut); if (connection_flag == false) { connection = open_connection(); connection_flag = true; // гонка! } } connection.send(data); } двойная проверка 49
  • 51. ˟˵˾˿˻́˱̃˾̌˺˳̌˸˿˳˹˿̃˼˿˷˶˾˾˱̐˹˾˹̇˹˱˼˹˸˱̇˹̐ class NetFacility { private: connect_handle connection; std::once_flag connection_flag; void open_connection() { connection = connect_manager.open(info); } public: NetFacility(connect_info _info): {} void send_data(data_packet const d) { // вызывается только один раз std::call_once(connection_flag, NetFacility::open_connection, this); connection.send(data); } void recv_data() { /* ... */ } } 50
  • 52. 5:˽̍̏̃˶˻̂̌˳ˢ class Widget { mutable std::shared_timed_mutex mut; int data; public: Widget operator=(const R rhs) { // эксклюзивные права на запись в *this std::unique_lockstd::shared_timed_mutex lhs(mut, std::defer_lock); // разделяемые права на чтение rhs std::shared_lockstd::shared_timed_mutex rhs(other.mut, std::defer_lock); std::lock(lhs, rhs); // выполнить присваивание data = rhs.data; return *this; } }; 51
  • 53. 5:˽̍̏̃˶˻̂̌˳ˢ int Widget::read() { std::shared_lockshared_timed_mutex lock(mut); return val; } void Widget::set_value(int _val) { std::lock_guardshared_mutex lock(mut); val = _val; } 52
  • 54. ˡ˶˻̄́̂˹˳˾̌˶˽̍̏̃˶˻̂̌ ▪ std::recursive_mutex ▪ мьютекс можно запирать несколько раз в одном потоке ▪ освобождать мьютекс требуется столько раз, сколько он был захвачен ▪ использование - аналогично std::mutex (std::lock_guard, std::unique_lock, …) 53
  • 55. ˤ̂˼˿˳˾̌˶̀˶́˶˽˶˾˾̌˶ ▪ std::condition_variable, std:: condition_variable_any условная переменная, необходимо взаимодействие с мьютексом (condition_variable) или с любым классом (condition_variable_any), подобным мьютексу ▪ wait - ожидание условия ▪ wait_for, wait_until - ожидание условия заданное время или до заданного момента ▪ notify_one - сообщить одному потоку ▪ notify_all - сообщить всем потокам 54
  • 56. ˤ̂˼˿˳˾̌˶̀˶́˶˽˶˾˾̌˶̀́˿˹˸˳˿˵˹̃˶˼̍̀˿̃́˶˲˹̃˶˼̍ std::mutex mut; std::queueWidget widget_queue; std::condition_variable cond; void producer() { for (;;) { Widget const w = get_request(); std::lock_guardstd::mutex lock(mut); widget_queue.push(data); cond.notify_one(); } } void consumer() { for (;;) { std::unique_lockstd::mutex lock(mut); cond.wait(lock, []{return !widget_queue.empty();}); Widget w = widget_queue.pop(); lock.unlock(); process(widget); } } 55
  • 61. int thinking(); // Запуск асинхронной (“фоновой”) задачи std::futureint answer = std::async(thinking); // Работа основного потока do_other_stuff(); // в этом время работает thinking() // Получение результатов std::cout The answer is answer.get() std::endl; main thread T1 answer.get() thinking... T2 работа ожидание async 58
  • 63. struct Widget { void foo(std::string const, int); int bar(std::string const); int operator()(int); }; Widget w; // Вызывается foo(carpe dieum, 2014) для объекта w auto f1 = std::async(Widget::foo, w, carpe diem, 2014); // Вызывается bar(carpe dieum, 2014) для объекта tmp = w auto f2 = std::async(Widget::bar, w, carpe diem); // Вызывается tmp.operator(2014), где tmp = w auto f3 = std::async(Widget(), 2014); // Вызвается w(1234) auto f4 = std::async(std::ref(w), 2014); 59
  • 65. struct Widget { Widget(); Widget(Widget); // Конструктор перемещения Widget(Widget const) = delete; // Запретить копирование // Оператор “перемещающее присваивание” Widget operator=(Widget); // Запретить присваивание Widget operator=(Widget const) = delete; void foo(std::string const, int); int bar(std::string const); int operator()(int); }; Widget w; auto f1 = std::async(Widget::foo, w, hi, 2014); auto f2 = std::async(Widget::bar, w, hi); auto f3 = std::async(Widget(), 2014); auto f4 = std::async(std::ref(w), 2014); 60
  • 67. ▪ std::launch::async - запуск функции в асинхронном режиме ▪ std::launch::deferred - запуск в момент вызова wait или get ▪ std::launch::async | std::launch::deferred - на усмотрение реализации (по умолчанию) auto f5 = std::async(std::launch::deferred, Widget::foo(), carpe diem, 2014); auto f6 = std::async(std::launch::deferred, Widget::bar(), carpe diem); auto f7 = std::async(std::launch::async, Widget(), 2014); std::cout f5.get() std::endl; // вызывается foo() f6.wait(); // вызывается bar() std::cout f7.get() std::endl; // только ожидание // результата 61
  • 68. ˤ̀˱˻˿˳˱˾˾̌˶˸˱˵˱̈˹ task 1 task 2 task 3 package 1 package 2 package 3 62
  • 69. ˤ̀˱˻˿˳˱˾˾̌˶˸˱˵˱̈˹ ▪ Шаблон std::packaged_task связывается будущий результат (future) с функцией ▪ Вызов функции происходит при вызове объекта packaged_task ▪ Параметр шаблона - сигнатура функции template class packaged_taskint(float, char) { public: templatetypename Callable explicit packaged_task(Callable func); std::futureint get_future(); void operator()(std::vectorchar*, int); }; пример спецификации шаблона для сигнатуры функции int func(float, char) 63
  • 71. task package task package add_task batch_system tasks.push_back( std::move(task)); std::packaged_taskvoid() task = std::move(tasks.front()); 64
  • 72. ˤ̀˱˻˿˳˱˾˾̌˶˸˱˵˱̈˹̀́˹˽˶́ std::mutex mut; std::dequestd::packaged_taskvoid() tasks; bool exit_flag = false; bool is_exit() { std::mutex mut; std::lock_guardstd::mutex lock(mut); return exit_flag; } void batch_system() { while (!is_exit()) { std::unique_lockstd::mutex lock(mut); if (tasks.empty()) continue; std::packaged_taskvoid() task = // получить упакованную std::move(tasks.front()); // задачу из очереди tasks.pop_front(); // удалить из очереди lock.unlock(); task(); // запуск задачи } } 65
  • 73. ˤ̀˱˻˿˳˱˾˾̌˶˸˱˵˱̈˹̀́˹˽˶́ templatetypename func std::futurevoid add_task(func f) { std::packaged_taskvoid() task(f); std::futurevoid res = task.get_future(); std::lock_guardstd::mutex lock(mut); tasks.push_back(std::move(task)); return res; } void say_vox() { std::cout voxn; } void say_populi() { std::cout populin; } void say_dei() { std::cout dein; } void write_word() { std::string s; std::cin s; } 66
  • 74. ˤ̀˱˻˿˳˱˾˾̌˶˸˱˵˱̈˹̀́˹˽˶́ int main() { std::thread batch(batch_system); add_task(say_vox); add_task(say_populi); add_task(write_word); add_task(say_vox); add_task(say_dei); std::this_thread::sleep_for( std::chrono::milliseconds(1000)); std::mutex mut; std::unique_lockstd::mutex lock(mut); exit_flag = true; lock.unlock(); batch.join(); return 0; } 67
  • 75. ˤ̀˱˻˿˳˱˾˾̌˶˸˱˵˱̈˹̀́˹˽˶́˳˿˸˽˿˷˾̌˶˳˱́˹˱˾̃̌ $ ./prog vox populi , $ ./prog vox populi , vox dei $ ./prog vox populi , vox 68
  • 77. ̀́˹˽˶́ void print_value(std::futureint fut) { int x = fut.get(); std::cout value: x std::endl; } int compute_value() { std::this_thread::sleep_for(std::chrono::seconds(1)); return 42; } int main () { std::promiseint prom; // Получаем объект future из созданного promise (обещаем) std::futureint fut = prom.get_future(); // Отправляем будущее значение в новый поток std::thread th1 (print_value, std::ref(fut)); int val = compute_value(); prom.set_value(val); // Выполняем обещание th1.join(); } 69
  • 79. ̀́˹˽˶́ main thread main prom.set_value() compute_value print_value print_value th1 th1(print_value, std::ref(fut)) fut.get() создание/завершение потоков синхронизация работа ожидание 70
  • 80. ˞˶˵˿̂̃˱̃˻˹̂˹˾̆́̇˹˹˾˱˿̂˾˿˳˶̄̂˼˿˳˾̌̆̀˶́˶˽˶˾˾̌̆ std::mutex mut; std::queueWidget widget_queue; std::condition_variable cond; void producer() { for (;;) { Widget const w = get_request(); std::lock_guardstd::mutex lock(mut); widget_queue.push(data); cond.notify_one(); } } void consumer() { for (;;) { std::unique_lockstd::mutex lock(mut); cond.wait(lock, []{return !widget_queue.empty();}); Widget w = widget_queue.pop(); lock.unlock(); process(widget); } } 71
  • 81. ˞˶˵˿̂̃˱̃˻˹̂˹˾̆́̇˹˹˾˱˿̂˾˿˳˶̄̂˼˿˳˾̌̆̀˶́˶˽˶˾˾̌̆ ▪ “Код с запашком” (code smell) Например, потоки выполняют код, который не нуждается в блокировке мьютекса (например, один поток инициализирует структуру, после чего сообщает другому, что структура готова). ▪ Пропущенный сигнал Поток может отправить сигнал (notify_one/all) в тот момент, когда другой поток не начал ожидать ▪ Ложное пробуждение (spurious wakeup) Поток, ожидающий сигнала может проснуться тогда, когда сигнал не был отправлен (или когда он был отправлен не этому потоку, или когда условие перестало выполняться). Нужна дополнительная проверка! (return !widget_queue.empty();) А что, если он не может проверить?! 72
  • 83. p.get_future().wait() I'm waiting... std::promise... p std::future 73
  • 85. p.get_future().wait() I'm waiting... ok, let’s move! p.set_value() std::promise... p std::future 74
  • 87. p.get_future().wait() I'm waiting... Можно отправить сигнал только один раз ok, let’s move! p.set_value() std::promise... p std::future 75
  • 89. ̀́˹˽˶́ std::promisevoid p; void react(); // реакция на условие void detect() { // обнаружение условия std::thread t([] { p.get_future().wait() react(); }); // делаем что-то // в это время t спит p.set_value(); // разбудить t // делаем ещё что-то t.join(); }; 76
  • 91. ̀́˹˽˶́ std::promisevoid p; void react(); // реакция на условие void detect() { // обнаружение условия std::thread t([] { p.get_future().wait() react(); }); // а что, если здесь возникнет исключение?? p.set_value(); // разбудить t // делаем ещё что-то t.join(); }; 77
  • 92. ˝˾˿˷˶̂̃˳˶˾˾˱̐˿̃̀́˱˳˻˱̂˹˴˾˱˼˿˳˹˼˹̀́˹˽˶́ std::promisevoid p; void detect() { auto sf = p.get_future().share(); std::vectorstd::thread vt; for (int i = 0; i threadsToRun; i++) { vt.emplace_back([sf]{ sf.wait(); react(); }); } // ... p.set_value(); // ... for (auto t: vt) t.join(); }; 78
  • 94. ̀́˹˽˶́ int main() { std::istringstream iss_numbers{3 1 42 23 -23 93 2 -289}; std::istringstream iss_letters{ a 23 b,e k k?a;si,ksa c}; std::vectorint numbers; std::vectorchar letters; std::promisevoid numbers_promise, letters_promise; auto numbers_ready = numbers_promise.get_future(); auto letter_ready = letters_promise.get_future(); std::thread value_reader([]{ std::copy(std::istream_iteratorint{iss_numbers}, std::istream_iteratorint{}, std::back_inserter(numbers)); numbers_promise.set_value(); std::copy_if(std::istreambuf_iteratorchar{iss_letters}, std::istreambuf_iteratorchar{}, std::back_inserter(letters), ::isalpha); letters_promise.set_value(); }); 79
  • 96. ̀́˹˽˶́ numbers_ready.wait(); // Ждать когда числа будут готовы std::sort(numbers.begin(), numbers.end()); if (letter_ready.wait_for(std::chrono::milliseconds(100)) == std::future_status::timeout) { // выводим числа, пока обрабатываются символы for (int num : numbers) std::cout num ' '; std::cout 'n'; numbers.clear(); // Числа уже были напечатаны } letter_ready.wait(); std::sort(letters.begin(), letters.end()); for (char let : letters) std::cout let ' '; std::cout 'n'; // If numbers were already printed, it does nothing. for (int num : numbers) std::cout num ' '; std::cout 'n'; value_reader.join(); } 80
  • 98. ̀́˹˽˶́ letter_ready.wait_for main main iss_numbers iss_letters работа ожидание value_ reader letters_promise. value_reader set_value() fut.get() number_ready.wait() sort sort output numbers_promise. set_value() создание/завершение потоков синхронизация 81
  • 100. ˳˱́˹˱˾̃̌ a a a a b c e i k k k s s -289 -23 1 2 3 4 23 42 93 93 -289 -23 1 2 3 23 42 93 a a a b c e i k k k s s 82
  • 101. ˡ˱˸˵˶˼̐˶˽̌˶˲̄˵̄̊˹˶́˶˸̄˼̍̃˱̃̌VKDUHGBIXWXUH int main() { std::promisevoid ready_promise, t1_ready_promise, t2_ready_promise; std::shared_futurevoid ready_future(ready_promise.get_future()); std::chrono::time_pointstd::chrono::high_resolution_clock start; auto fun1 = []() - std::chrono::durationdouble, std::milli { t1_ready_promise.set_value(); ready_future.wait(); // ожидать сигнала из main() return std::chrono::high_resolution_clock::now() - start; }; auto fun2 = []() - std::chrono::durationdouble, std::milli { t2_ready_promise.set_value(); ready_future.wait(); // ожидать сигнала из main() return std::chrono::high_resolution_clock::now() - start; }; 83
  • 102. ˡ˱˸˵˶˼̐˶˽̌˶˲̄˵̄̊˹˶́˶˸̄˼̍̃˱̃̌VKDUHGBIXWXUH auto result1 = std::async(std::launch::async, fun1); auto result2 = std::async(std::launch::async, fun2); // ждать, пока потоки не будут готовы t1_ready_promise.get_future().wait(); t2_ready_promise.get_future().wait(); // потоки готовы - начать отчёт времени start = std::chrono::high_resolution_clock::now(); // запустить потоки ready_promise.set_value(); std::cout Thread 1 received the signal result1.get().count() ms after startn Thread 2 received the signal result2.get().count() ms after startn; } 84
  • 103. ˡ˱˸˵˶˼̐˶˽̌˶˲̄˵̄̊˹˶́˶˸̄˼̍̃˱̃̌VKDUHGBIXWXUH main создание/завершение потоков синхронизация работа ожидание T1 T2 t2_ready. set_value start return return output ready. set_value t1_ready. set_value 85
  • 105. ˒̌̂̃́˱̐̂˿́̃˹́˿˳˻˱˳˵̄̆˶̅̄˾˻̇˹˿˾˱˼̍˾˿˴˿̀́˿˴̐ templatetypename T std::listT sequential_quick_sort(std::listT input) { if (input.empty()) { return input; } std::listT result; result.splice(result.begin(), input, input.begin()); T const pivot = *result.begin(); auto divide_point = std::partition(input.begin(), input.end(), [](T const t){return t pivot; }); std::listT lower_part; lower_part.splice(lower_part.end(), input, input.begin(), divide_point); auto new_lower( sequential_quick_sort(std::move(lower_part))); auto new_higher( sequential_quick_sort(std::move(input))); result.splice(result.end(), new_higher); result.splice(result.begin(), new_lower); return result; } 87
  • 106. ˒̌̂̃́˱̐̂˿́̃˹́˿˳˻˱˳˵̄̆˶̅̄˾˻̇˹˿˾˱˼̍˾˿˴˿̀́˿˴̐ lower part input pivot input splice new_lower divide_point new_higher 88
  • 107. ˠ˱́˱˼˼˶˼̍˾̌˺˱˼˴˿́˹̃˽˲̌̂̃́˿˺̂˿́̃˹́˿˳˻˹ templatetypename T std::listT parallel_quick_sort(std::listT input) { if (input.empty()) { return input; } std::listT result; result.splice(result.begin(), input, input.begin()); T const pivot = *result.begin(); auto divide_point = std::partition(input.begin(), input.end(), [](T const t){return t pivot; }); std::listT lower_part; lower_part.splice(lower_part.end(), input, input.begin(), divide_point); std::futurestd::listT new_lower( std::async(parallel_quick_sort, std::move(lower_part))); auto new_higher( parallel_quick_sort(std::move(input))); result.splice(result.end(), new_higher); result.splice(result.begin(), new_lower.get()); return result; } 89
  • 110. auto func1() { std::cout begin thinking over the answer...n; std::this_thread::sleep_for(dur3); return 40; } auto func2(int x) { std::cout continue thinking over the answer...n; std::this_thread::sleep_for(dur1); return x + 2; } auto func3(int x) { std::cout still thinking...n; std::this_thread::sleep_for(dur2); return number + std::to_string(x); } void do_some_stuff() { std::cout do some useful stuff; } void do_some_other_stuff() { std::cout do other stuff; } 91
  • 112. int main() { auto f1 = std::async(func1); auto f2 = std::async(func2, f1.get()); auto f3 = std::async(func3, f2.get()); std::cout waiting for the answer...n; do_some_stuff(); std::cout answer: f3.get() std::endl; do_some_other_stuff(); 92
  • 114. int main() { auto f1 = std::async(func1); auto f2 = std::async(func2, f1.get()); auto f3 = std::async(func3, f2.get()); std::cout waiting for the answer...n; do_some_stuff(); std::cout answer: f3.get() std::endl; do_some_other_stuff(); Каждый раз после получения результата выполняется создание новой асинхронной задачи. Поток может быть заблокирован при вызове get() для ожидания результата. 93
  • 116. $ ./prog begin thinking over the answer... continue thinking over the answer... waiting for the answer... do some useful stuff answer: still thinking... number 42 do some other useful stuff 94
  • 118. #define BOOST_THREAD_PROVIDES_FUTURE #define BOOST_THREAD_PROVIDES_FUTURE_CONTINUATION #include boost/thread/future.hpp int main() { auto f = boost::async([](){ return func1(); }).then([](auto f){ return func2(f.get()); }).then([](auto f){ return func3(f.get()); }); std::cout waiting for the answer...n; do_some_stuff(); f.then([](auto f){ std::cout answer: f.get() std::endl; }); do_some_other_stuff(); 95
  • 120. #define BOOST_THREAD_PROVIDES_FUTURE #define BOOST_THREAD_PROVIDES_FUTURE_CONTINUATION #include boost/thread/future.hpp int main() { auto f = boost::async([](){ return func1(); }).then([](auto f){ return func2(f.get()); }).then([](auto f){ return func3(f.get()); }); std::cout waiting for the answer...n; do_some_stuff(); f.then([](auto f){ std::cout answer: f.get() std::endl; вызывающий поток не блокируется }); do_some_other_stuff(); 96
  • 122. #define BOOST_THREAD_PROVIDES_FUTURE #define BOOST_THREAD_PROVIDES_FUTURE_CONTINUATION #include boost/thread/future.hpp int main() { auto f = boost::async([](){ return func1(); }).then([](auto f){ return func2(f.get()); }).then([](auto f){ return func3(f.get()); }); std::cout waiting for the answer...n; do_some_stuff(); f.then([](auto f){ std::cout answer: f.get() std::endl; вызывающий поток блокируется }).wait(); do_some_other_stuff(); 97
  • 124. $ g++ -Wall -pedantic -pthread -lboost_system -lboost_thread -std=c++14 -O2 prog.cpp -o prog $ ./prog waiting for the answer... do some useful stuff begin thinking over the answer... continue thinking over the answer... still thinking... answer: number 42 do some other useful stuff 98
  • 126. Блокирующие future Неблокирующие future f2 f3 f1 f ▪ порядок выполнения ▪ устанавливается явный неопределён порядок выполнения ▪ возможны блокировки ▪ нет блокировок ▪ для каждой задачи ▪ поток один создаётся отдельный поток 99
  • 128. #define BOOST_THREAD_PROVIDES_FUTURE_WHEN_ALL_WHEN_ANY #include boost/thread/future.hpp std::vectorboost::futurevoid task_chunk; task_chunk.emplace_back(boost::async([]() { std::cout hello from task 1n; })); task_chunk.emplace_back(boost::async([]() { std::cout hello from task 2n; })); task_chunk.emplace_back(boost::async([]() { std::cout hello from task 3n; })); auto join_task = boost::when_all(task_chunk.begin(), task_chunk.end()); do_some_stuff(); join_task.wait(); 100
  • 130. f2 f1 f3 f4 Будущий результат f4 зависит от выполнения всех будущих результатов f1, f2, f3 и начинает выполняться после завершения выполнения задач, им соответствующих (подобно барьерной синхронизации). 101
  • 132. std::vectorboost::futureint task_chunk; task_chunk.emplace_back(boost::async(boost::launch::async, [](){ std::cout hello from task 1n; return 10; })); task_chunk.emplace_back(boost::async(boost::launch::async, [](){ std::cout hello from task 2n; return 20; })); task_chunk.emplace_back(boost::async(boost::launch::async, [](){ std::cout hello from task 3n; return 12; })); auto join_task = boost::when_all(task_chunk.begin(), task_chunk.end()) .then([](auto results){ auto res = 0; for (auto elem: results.get()) res += elem.get(); return res; join_task имеет тип future vector }); do_some_stuff(); std::cout result: join_task.get() std::endl; futureT 102
  • 134. f2 f1 f3 f4 f5 103
  • 136. $ g++ -Wall -pedantic -pthread -lboost_system -lboost_thread -std=c++14 -O2 prog.cpp -o prog $ ./prog hello from task 1 hello from task 3 hello from task 2 do some useful stuff result: 42 104
  • 138. std::vectorboost::futuredecltype(M_PI) task_chunk; task_chunk.emplace_back(boost::async(boost::launch::async, []() { std::this_thread::sleep_for(dur1); return M_PI; })); task_chunk.emplace_back(boost::async(boost::launch::async, []() { std::this_thread::sleep_for(dur2); return M_E; })); task_chunk.emplace_back(boost::async(boost::launch::async, []() { std::this_thread::sleep_for(dur3); return M_LN2; })); auto join_task = boost::when_any(task_chunk.begin(), task_chunk.end()) .then([](auto results) { for (auto elem: results.get()) { if (elem.is_ready()) { return elem.get(); } } exit(1); // this will never happen }); do_some_stuff(); std::cout result: join_task.get() std::endl; 105
  • 140. f2 f1 f3 f4 Будущий результат f4 зависит от выполнения одного из будущих результатов f1, f2, f3 и начинает выполняться после завершения выполнения хотя бы одной задачи (подобно синхронизации “эврика”). 106
  • 142. $ g++ -Wall -pedantic -pthread -lboost_system -lboost_thread -std=c++14 -O2 prog.cpp -o prog do some useful stuff result: 2.71828 do some useful stuff result: 0.693147 do some useful stuff result: 3.14159 107
  • 144. ˝̍̏̃˶˻̂̌˹˿̈˶́˶˵˹˸˱˵˱̈ Блокировка мьютекса Очереди задач class Logger { std::fstream flog; public: void writelog(...) { flog current_time() : logmsg std::endl; } }; class Logger { std::fstream flog; public: void writelog(...) { flog current_time() : logmsg std::endl; } }; 109
  • 145. ˝̍̏̃˶˻̂̌˹˿̈˶́˶˵˹˸˱˵˱̈ Блокировка мьютекса Очереди задач class Logger { std::fstream flog; std::mutex mut; public: void writelog(...) { std::lock_guard std::mutex lock(mut); flog current_time() : logmsg std::endl; } }; class Logger { std::fstream flog; worker_thread worker; public: void writelog(...) { worker.send([=]{ flog current_time() : logmsg std::endl; }); } }; 110
  • 146. ˝̍̏̃˶˻̂̌˹˿̈˶́˶˵˹˸˱˵˱̈ Блокировка мьютекса ▪ потоки блокируются ▪ имеется возможность дедлока ▪ небольшая масштабируемость ▪ порядок следования сообщения в логе отличается от последовательности поступления Очереди задач ▪ потоки не блокируются ▪ отсутствует возможность дедлока ▪ высокая масштабируемость ▪ порядок следования сообщения в логе совпадает с фактическим 111
  • 147. ˠ˱̃̃˶́˾̀˿̃˿˻˿˲˶˸˿̀˱̂˾˱̐˿˲̒́̃˻˱˾˱˵˵˱˾˾̌˽˹ Требования к потокобезопасным “обёрткам”: 1. Сохранение интерфейса widget w; = w.func(hi folks!); wrapperwidget = w.func(hi folks!); 2. Универсальность. Заранее может быть неизвестны методы, которые необходимо обернуть. Некоторые методы сложно обернуть: конструкторы, операторы, шаблоны и т.д. 3. Поддержка транзакций account.deposit(Sergey, 1000) account.withdraw(Ivan, 1000); log.print(user , username, data ); log.print(time , logmsg); Реализация отдельных методов может не обеспечить необходимую гранулярность. 112
  • 148. ˠ˱̃̃˶́˾˿˲̒́̃˻˱˾˱˵˵˱˾˾̌˽˹̂˲˼˿˻˹́˿˳˻˱˽˹ templatetypename T class wrapper { private: T t; // оборачиваемый объект ... // состояние враппера public: monitor(T _t): t(_t) { } template typename F // 1. получаем любую функцию // 2. подставляем в неё оборачиваемый объект // 3. выполняем и возвращаем результат auto operator()(F f) - decltype(f(t)) { // работа враппера auto ret = f(t); // ... return ret; } }; 113
  • 149. ˠ˿̃˿˻˿˲˶˸˿̀˱̂˾˱̐˿˲̒́̃˻˱˾˱˵˵˱˾˾̌˽˹̂˲˼˿˻˹́˿˳˻˱˽˹ templatetypename T class monitor { private: T t; std::mutex m; public: monitor(T _t): t(_t) { } template typename F auto operator()(F f) - decltype(f(t)) { std::lock_guardstd::mutex lock(m); // вызов “объявления” под защитой мьютекса return f(t); } }; 114
  • 150. ˠ˿̃˿˻˿˲˶˸˿̀˱̂˾˱̐˿˲̒́̃˻˱˾˱˵˵˱˾˾̌˽˹̂˲˼˿˻˹́˿˳˻˱˽˹ monitorstd::string smon{start }; // инициализация std::vectorstd::futurevoid v; for (auto i = 0; i 5; i++) { // выполнить асинхр. задачи... v.emplace_back(std::async(std::launch::async, [, i]{ smon([=](auto s){ // объявление функции s += i = + std::to_string(i); s += ; }); smon([](auto s){ // объявление функции std::cout s std::endl; }); })); } for (auto f: v) // дождаться завершения f.wait(); std::cout donen; 115
  • 151. ˠ˿̃˿˻˿˲˶˸˿̀˱̂˾˱̐˿˲̒́̃˻˱˾˱˵˵˱˾˾̌˽˹̂˲˼˿˻˹́˿˳˻˱˽˹ start i = 1 start i = 1 i = 0 start i = 1 i = 0 i = 2 start i = 1 i = 0 i = 2 i = 4 start i = 1 i = 0 i = 2 i = 4 i = 3 done start i = 0 start i = 0 i = 2 start i = 0 i = 2 i = 3 start i = 0 i = 2 i = 3 i = 1 start i = 0 i = 2 i = 3 i = 1 i = 4 done start i = 0 start i = 0 i = 2 start i = 0 i = 2 i = 4 start i = 0 i = 2 i = 4 i = 1 start i = 0 i = 2 i = 4 i = 1 i = 3 done 116
  • 152. ˠ˿̃˿˻˿˲˶˸˿̀˱̂˾˱̐˿˲̒́̃˻˱˾˱˵˵˱˾˾̌˽˹̂˲˼˿˻˹́˿˳˻˱˽˹ monitorstd::ostream mon_cout{std::cout}; std::vectorstd::futurevoid v; for (auto i = 0; i 5; i++) { v.emplace_back(std::async(std::launch::async, [, i]{ mon_cout([=](auto cout){ cout i = std::to_string(i); cout n; }); mon_cout([=](auto cout){ cout hi from i std::endl; }); })); } for (auto f: v) f.wait(); mon_cout([](auto cout){ cout donen; }); 117
  • 153. ˠ˿̃˿˻˿˲˶˸˿̀˱̂˾˱̐˿˲̒́̃˻˱˾˱˵˵˱˾˾̌˽˹̂˲˼˿˻˹́˿˳˻˱˽˹ i = 0 i = 2 hi from 2 hi from 0 i = 1 hi from 1 i = 3 hi from 3 i = 4 hi from 4 done i = 0 i = 3 i = 2 hi from 2 i = 1 hi from 1 hi from 3 i = 4 hi from 4 hi from 0 done i = 0 hi from 0 i = 4 hi from 4 i = 2 hi from 2 i = 3 hi from 3 i = 1 hi from 1 done i = 0 hi from 0 i = 2 hi from 2 i = 3 hi from 3 i = 1 hi from 1 i = 4 hi from 4 done 118
  • 154. ˠ˿̃˿˻˿˲˶˸˿̀˱̂˾˱̐˿˲̒́̃˻˱˾˱˵˵˱˾˾̌˽˹˾˱˿̂˾˿˳˶˿̈˶́˶˵˹˸˱˵˱̈ templatetypename T class concurrent { private: T t; concurrent_queuestd::functionvoid() q; bool done = false; std::thread thd; public: concurrent(T t_): t{t_}, thd{[=]{ while (!done) { (*q.wait_and_pop())(); } } } { } ~concurrent() { q.push([=]{ done = true; }); thd.join(); } templatetypename F void operator()(F f) { q.push([=]{ f(t); }); } }; 119
  • 155. ˠ˿̃˿˻˿˲˶˸˿̀˱̂˾˱̐˿˲̒́̃˻˱˾˱˵˵˱˾˾̌˽˹˾˱˿̂˾˿˳˶˿̈˶́˶˵˹˸˱˵˱̈ concurrentstd::string smon{start }; // инициализация std::vectorstd::futurevoid v; for (auto i = 0; i 5; i++) { // выполнить асинхр. задачи... v.emplace_back(std::async(std::launch::async, [, i]{ smon([=](auto s){ // объявление функции s += i = + std::to_string(i); s += ; }); smon([](auto s){ // объявление функции std::cout s std::endl; }); })); } for (auto f: v) // дождаться завершения f.wait(); std::cout donen; 120
  • 156. ˠ˿̃˿˻˿˲˶˸˿̀˱̂˾˱̐˿˲̒́̃˻˱˾˱˵˵˱˾˾̌˽˹˾˱˿̂˾˿˳˶˿̈˶́˶˵˹˸˱˵˱̈ start i = 0 start i = 0 i = 2 start i = 0 i = 2 i = 3 start i = 0 i = 2 i = 3 i = 1 start i = 0 i = 2 i = 3 i = 1 i = 4 done start i = 0 done start i = 0 i = 2 start i = 0 i = 2 i = 1 start i = 0 i = 2 i = 1 i = 3 start i = 0 i = 2 i = 1 i = 3 i = 4 start i = 0 start i = 0 i = 1 start i = 0 i = 1 i = 4 start i = 0 i = 1 i = 4 i = 3 start i = 0 i = 1 i = 4 i = 3 i = 2 done 121
  • 157. ˠ˿̃˿˻˿˲˶˸˿̀˱̂˾˱̐˿˲̒́̃˻˱˾˱˵˵˱˾˾̌˽˹˾˱˿̂˾˿˳˶˿̈˶́˶˵˹˸˱˵˱̈ concurrentstd::ostream mon_cout{std::cout}; std::vectorstd::futurevoid v; for (auto i = 0; i 5; i++) { v.emplace_back(std::async(std::launch::async, [, i]{ mon_cout([=](auto cout){ cout i = std::to_string(i); cout n; }); mon_cout([=](auto cout){ cout hi from i std::endl; }); })); } for (auto f: v) f.wait(); mon_cout([](auto cout){ cout donen; }); 122
  • 158. ˠ˿̃˿˻˿˲˶˸˿̀˱̂˾˱̐˿˲̒́̃˻˱˾˱˵˵˱˾˾̌˽˹˾˱˿̂˾˿˳˶˿̈˶́˶˵˹˸˱˵˱̈ i = 0 hi from 0 i = 2 hi from 2 i = 3 hi from 3 i = 4 hi from 4 i = 1 hi from 1 done i = 0 hi from 0 i = 2 hi from 2 i = 3 hi from 3 i = 4 hi from 4 i = 1 hi from 1 done i = 0 i = 1 hi from 1 hi from 0 i = 2 hi from 2 i = 4 hi from 4 i = 3 hi from 3 done i = 0 hi from 0 i = 2 hi from 2 i = 1 i = 3 hi from 3 hi from 1 i = 4 hi from 4 done 123
  • 159. ˠ˿̃˿˻˿˲˶˸˿̀˱̂˾˱̐˿˲̒́̃˻˱˾˱˵˵˱˾˾̌˽˹˾˱˿̂˾˿˳˶˿̈˶́˶˵˹˸˱˵˱̈ Данная версия operator() позволяет вернуть значение при вызове функции: templatetypename F auto operator()(F f) - std::futuredecltype(f(t)) { // создаём объект promise (shared_ptrpromise) auto p = std::make_sharedstd::promisedecltype(f(t))(); // получаем из promise объект future auto ret = p-get_future(); q.push([=]{ // выполняем обещание уже внутри потока try { p-set_value(f(t)); } catch (...) { p-set_exception(std::current_exception()); } }); return ret; } 124
  • 160. ˠ˿̃˿˻˿˲˶˸˿̀˱̂˾˱̐˿˲̒́̃˻˱˾˱˵˵˱˾˾̌˽˹˾˱˿̂˾˿˳˶˿̈˶́˶˵˹˸˱˵˱̈ templatetypename F auto operator()(F f) - std::futuredecltype(f(t)) { auto p = std::make_sharedstd::promisedecltype(f(t))(); auto ret = p-get_future(); q.push([=]{ try { set_value(*p, f, t); } catch (...) { p-set_exception(std::current_exception()); } }); return ret; } templatetypename Fut, typename F, typename T1 void set_value(std::promiseFut p, F f, T1 t) { p.set_value(f(t)); } templatetypename F, typename T1 void set_value(std::promisevoid p, F f, T1 t) { f(t); p.set_value(); } 125
  • 161. ˠ˿̃˿˻˿˲˶˸˿̀˱̂˾˱̐˿˲̒́̃˻˱˾˱˵˵˱˾˾̌˽˹˾˱˿̂˾˿˳˶˿̈˶́˶˵˹˸˱˵˱̈ templatetypename F auto operator()(F f) - std::futuredecltype(f(t)) { auto p = std::make_sharedstd::promisedecltype(f(t))(); auto ret = p-get_future(); q.push([=]{ try { set_value(*p, f, t); } catch (...) { p-set_exception(std::current_exception()); } }); return ret; } auto f = smon([](auto s){ s += donen; std::cout s; return s; }); std::cout return value: f.get() std::endl; 126
  • 162. ˠ˿̃˿˻˿˲˶˸˿̀˱̂˾˱̐˿˲̒́̃˻˱˾˱˿̂˾˿˳˶˿̈˶́˶˵˹˸˱˵˱̈̀́˹˽˶˾˶˾˹˶ class backgrounder { public: futurebool save(std::string file) { c([=](data d) { ... // каждая функция - в отдельной транзакции }); } futuresize_t print(some stuff) { c([=, stuff](data d) { ... // атомарный неделимый вывод }); } private: struct data { /* ... */ } // данные concurrentdata c; // обёртка для потокобезопасного }; // выполнения операций с данными 127
  • 164. ˧˶˼̍́˱˸́˱˲˿̃˻˹̀˱́˱˼˼˶˼̍˾̌̆̂̃́̄˻̃̄́˵˱˾˾̌̆ ▪ Обеспечить параллельный доступ ▪ Обеспечить безопасность доступа ▪ Минимизировать взаимные исключения ▪ Минимизировать сериализацию 129
  • 165. ˧˶˼̍́˱˸́˱˲˿̃˻˹̀˱́˱˼˼˶˼̍˾̌̆̂̃́̄˻̃̄́˵˱˾˾̌̆ Задачи проектирования структур данных с блокировками: ▪ Ни один поток не может увидеть состояние, в котором инварианты нарушены ▪ Предотвратить состояние гонки ▪ Предусмотреть возникновение исключений ▪ Минимизировать возможность взаимоблокировок Средства достижения: ▪ ограничить область действия блокировок ▪ защитить разные части структуры разными мьютексами ▪ обеспечить разный уровень защиты ▪ изменить структуру данных для расширения возможностей распраллеливания 130
  • 166. ˠ˿̃˿˻˿˲˶˸˿̀˱̂˾̌˺̂̃˶˻̀˿̃˶˾̇˹˱˼̍˾̌˶̀́˿˲˼˶˽̌ Потенциальные проблемы безопасности реализации потокобезопасных структур: 1. Гонки данных 2. Взаимоблокировки 3. Безопасность относительно исключений 4. ... 131
  • 167. ˠ˿̃˿˻˿˲˶˸˿̀˱̂˾̌˺̂̃˶˻ struct empty_stack: std::exception { }; templatetypename T class threadsafe_stack { private: std::stackT data; mutable std::mutex m; public: threadsafe_stack() {} threadsafe_stack(const threadsafe_stack other) { std::lock_guardstd::mutex lock(other.m); data = other.data; } threadsafe_stack operator=(const threadsafe_stack) = delete; void push(T new_value) { std::lock_guardstd::mutex lock(m); data.push(std::move(new_value)); } Защита данных 132
  • 168. ˠ˿̃˿˻˿˲˶˸˿̀˱̂˾̌˺̂̃˶˻ T pop() { std::lock_guardstd::mutex lock(m); if (data.empty()) throw empty_stack(); auto value = data.top(); data.pop(); return value; } bool empty() const { std::lock_guardstd::mutex lock(m); return data.empty(); } }; 133
  • 169. ˠ˿̃˿˻˿˲˶˸˿̀˱̂˾̌˺̂̃˶˻̃˶̂̃˿˳˱̐̀́˿˴́˱˽˽˱ threadsafe_stackint stack; void pusher(unsigned nelems) { for (unsigned i = 0; i nelems; i++) { stack.push(i); } } void printer() { try { for (;;) { int val; stack.pop(val); } } catch (empty_stack) { std::cout stack is empty! std::endl; } } int main() { std::thread t1(pusher, 5), t2(pusher, 5); t1.join(); t2.join(); std::thread t3(printer); t3.join(); } 134
  • 170. ˠ˿̃˿˻˿˲˶˸˿̀˱̂˾̌˺̂̃˶˻˲˶˸˿̀˱̂˾˿̂̃̍˹̂˻˼̏̈˶˾˹˺ T pop() { 1 std::lock_guardstd::mutex lock(m); if (data.empty()) throw empty_stack(); auto value = data.top(); data.pop(); return value; } 2 3 [невозвратная] модификация контейнера 4 135
  • 171. ˓˶́̂˹̐SRS˲˶˸˿̀˱̂˾˱̐̂̃˿̈˻˹˸́˶˾˹̐˹̂˻˼̏̈˶˾˹˺ std::shared_ptrT pop() { std::lock_guardstd::mutex lock(m); if (data.empty()) throw empty_stack(); std::shared_ptrT const res( std::make_sharedT(std::move(data.top()))); data.pop(); return res; } void pop(T value) { [невозвратная] модификация контейнера std::lock_guardstd::mutex lock(m); if (data.empty()) throw empty_stack(); value = std::move(data.top()); data.pop(); } 1 2 3 4 5 6 [невозвратная] модификация контейнера 136
  • 172. ˠ˿̃˿˻˿˲˶˸˿̀˱̂˾̌˺̂̃˶˻˳˸˱˹˽˿˲˼˿˻˹́˿˳˻˹ struct empty_stack: std::exception { }; templatetypename T class threadsafe_stack { private: std::stackT data; mutable std::mutex m; public: threadsafe_stack() {} threadsafe_stack(const threadsafe_stack other) { std::lock_guardstd::mutex lock(other.m); data = other.data; } threadsafe_stack operator=(const threadsafe_stack) = delete; void push(T new_value) { std::lock_guardstd::mutex lock(m); data.push(std::move(new_value)); } '($'/2. 137
  • 173. ˠ˿̃˿˻˿˲˶˸˿̀˱̂˾̌˺̂̃˶˻˳˸˱˹˽˿˲˼˿˻˹́˿˳˻˹ std::shared_ptrT pop() { std::lock_guardstd::mutex lock(m); if (data.empty()) throw empty_stack(); std::shared_ptrT const res( std::make_sharedT(std::move(data.top()))); data.pop(); return res; } void pop(T value) { std::lock_guardstd::mutex lock(m); if (data.empty()) throw empty_stack(); value = std::move(data.top()); data.pop(); } bool empty() const { std::lock_guardstd::mutex lock(m); return data.empty(); } }; '($'/2. '($'/2. 138
  • 174. ˠ˿̃˿˻˿˲˶˸˿̀˱̂˾̌˺̂̃˶˻̃˶̂̃˿˳˱̐̀́˿˴́˱˽˽˱ threadsafe_stackint stack; void pusher(unsigned nelems) { for (unsigned i = 0; i nelems; i++) { stack.push(i); } } void printer() { try { for (;;) { int val; stack.pop(val); } } catch (empty_stack) { std::cout stack is empty! std::endl; } } int main() { std::thread t1(pusher, 5), t2(pusher, 5); t1.join(); t2.join(); std::thread t3(printer); t3.join(); } Недостатки реализации: ▪ Сериализация потоков приводит к снижению производительности: потоки простаивают и не совершают полезной работы ▪ Нет средств, позволяющих ожидать добавления элемента 139
  • 175. ˠ˿̃˿˻˿˲˶˸˿̀˱̂˾˱̐˿̈˶́˶˵̍̂˿˷˹˵˱˾˹˶˽ templatetypename T class threadsafe_queue { private: mutable std::mutex mut; std::queueT data_queue; std::condition_variable data_cond; public: threadsafe_queue() {} void push(T new_value) { std::lock_guardstd::mutex lk(mut); data_queue.push(std::move(new_value)); data_cond.notify_one(); } std::shared_ptrT wait_and_pop() { std::unique_lockstd::mutex lk(mut); data_cond.wait(lk, [this]{return !data_queue.empty();}); std::shared_ptrT res( std::make_sharedT(std::move(data_queue.front()))); data_queue.pop(); return res; } 140
  • 176. ˠ˿̃˿˻˿˲˶˸˿̀˱̂˾˱̐˿̈˶́˶˵̍̂˿˷˹˵˱˾˹˶˽ void wait_and_pop(T value) { std::unique_lockstd::mutex lk(mut); data_cond.wait(lk, [this]{return !data_queue.empty();}); value = std::move(data_queue.front()); data_queue.pop(); } bool try_pop(T value) { std::lock_guardstd::mutex lk(mut); if (data_queue.empty()) return false; value = std::move(data_queue.front()); data_queue.pop(); return true; } std::shared_ptrT try_pop() { // ... } bool empty() const { /* ... */ } 141
  • 177. ˠ˿̃˿˻˿˲˶˸˿̀˱̂˾˱̐˿̈˶́˶˵̍̃˶̂̃˿˳˱̐̀́˿˴́˱˽˽˱ threadsafe_queueint queue; void pusher(unsigned nelems) { for (unsigned i = 0; i nelems; i++) { queue.push(i); } } void poper(unsigned nelems) { for (unsigned i = 0; i nelems; i++) { int val; queue.wait_and_pop(val); } } int main() { std::thread t1(pusher, 5), t2(pusher, 5), t3(poper, 9); t1.join(); t2.join(); t3.join(); } Не требуется проверка empty() 142
  • 178. ˠ˿̃˿˻˿˲˶˸˿̀˱̂˾˱̐˿̈˶́˶˵̍̂˿˷˹˵˱˾˹˶˽ void wait_and_pop(T value) { std::unique_lockstd::mutex lk(mut); data_cond.wait(lk, [this]{return !data_queue.empty();}); value = std::move(data_queue.front()); data_queue.pop(); } bool try_pop(T value) { std::lock_guardstd::mutex lk(mut); if (data_queue.empty()) return false; value = std::move(data_queue.front()); data_queue.pop(); return true; } std::shared_ptrT try_pop() { // ... } bool empty() const { /* ... */ } Не вызывается исключение 143
  • 179. ˟̈˶́˶˵̍̂˿˷˹˵˱˾˹˶˽˲˶˸˿̀˱̂˾˿̂̃̍˹̂˻˼̏̈˶˾˹˺ templatetypename T class threadsafe_queue { private: mutable std::mutex mut; std::queueT data_queue; std::condition_variable data_cond; public: threadsafe_queue() {} void push(T new_value) { std::lock_guardstd::mutex lk(mut); data_queue.push(std::move(new_value)); data_cond.notify_one(); } std::shared_ptrT wait_and_pop() { std::unique_lockstd::mutex lk(mut); data_cond.wait(lk, [this]{return !data_queue.empty();}); std::shared_ptrT res( std::make_sharedT(std::move(data_queue.front()))); data_queue.pop(); return res; } При срабатывании исключения в wait_and_pop (в ходе инициализации res) другие потоки не будут разбужены 144
  • 180. ˠ˿̃˿˻˿˲˶˸˿̀˱̂˾˱̐˿̈˶́˶˵̍˽˿˵˹̅˹̇˹́˿˳˱˾˾˱̐˳˶́̂˹̐ templatetypename T class threadsafe_queue { private: mutable std::mutex mut; std::queuestd::shared_ptrT data_queue; std::condition_variable data_cond; public: void push(T new_value) { std::shared_ptrT data( std::make_sharedT(std::move(new_value))); std::lock_guardstd::mutex lk(mut); data_queue.push(std::move(new_value)); data_cond.notify_one(); } std::shared_ptrT wait_and_pop() { std::unique_lockstd::mutex lk(mut); data_cond.wait(lk, [this]{ return !data_queue.empty(); }); std::shared_ptrT res = data_queue.front(); data_queue.pop(); return res; } Очередь теперь хранит элементы shared_ptr Инициализация объекта теперь выполняется не под защитой блокировки (и это весьма хорошо) Объект извлекается напрямую 145
  • 181. ˠ˿̃˿˻˿˲˶˸˿̀˱̂˾˱̐˿̈˶́˶˵̍˽˿˵˹̅˹̇˹́˿˳˱˾˾˱̐˳˶́̂˹̐ void wait_and_pop(T value) { std::unique_lockstd::mutex lk(mut); data_cond.wait(lk, [this]{return !data_queue.empty();}); value = std::move(*data_queue.front()); data_queue.pop(); } bool try_pop(T value) { std::lock_guardstd::mutex lk(mut); if (data_queue.empty()) return false; value = std::move(*data_queue.front()); data_queue.pop(); return true; } std::shared_ptrT try_pop() { // ... } bool empty() const { /* ... */ } Объект извлекается из очереди напрямую, shared_ptr не инициализируется - исключение не возбуждается! 146
  • 182. ˠ˿̃˿˻˿˲˶˸˿̀˱̂˾˱̐˿̈˶́˶˵̍˽˿˵˹̅˹̇˹́˿˳˱˾˾˱̐˳˶́̂˹̐ std::shared_ptrT wait_and_pop() { std::unique_lockstd::mutex lk(mut); data_cond.wait(lk, [this]{ return !data_queue.empty(); }); std::shared_ptrT res = data_queue.front(); data_queue.pop(); return res; Недостатки реализации: ▪ Сериализация потоков приводит к снижению } bool try_pop(T value) { std::lock_guardstd::mutex lk(mut); if (data_queue.empty()) return false; value = std::move(*data_queue.front()); data_queue.pop(); return true; } std::shared_ptrT try_pop() { // ... } bool empty() const { /* ... */ } Объект извлекается из очереди напрямую, shared_ptr не инициализируется производительности: потоки простаивают и не совершают полезной работы 147
  • 184. ˟̈˶́˶˵̍̂˽˶˼˻˿˸˶́˾˹̂̃̌˽˹˲˼˿˻˹́˿˳˻˱˽˹ templatetypename T class queue { private: struct node { T data; std::unique_ptrnode next; node(T _data): data(std::move(_data)) {} }; std::unique_ptrnode head; node* tail; public: Использование unique_ptrnode гарантирует удаление узлов без использования delete queue() {} queue(const queue other) = delete; queue operator=(const queue other) = delete; 149
  • 185. ˟̈˶́˶˵̍̂˽˶˼˻˿˸˶́˾˹̂̃̌˽˹˲˼˿˻˹́˿˳˻˱˽˹ std::shared_ptrT try_pop() { if (!head) { return std::shared_ptrT(); } std::shared_ptrT const res( std::make_sharedT(std::move(head-data))); std::unique_ptrnode const old_head = std::move(head); head = std::move(old_head-next); return res; } void push(T new_value) { std::unique_ptrnode p(new node(std::move(new_value))); node* const new_tail = p.get(); if (tail) tail-next = std::move(p); else head = std::move(p); tail = new_tail; } }; 150
  • 186. ˟̈˶́˶˵̍̂˽˶˼˻˿˸˶́˾˹̂̃̌˽˹˲˼˿˻˹́˿˳˻˱˽˹ std::shared_ptrT try_pop() { if (!head) { return std::shared_ptrT(); } std::shared_ptrT const res( std::make_sharedT(std::move(head-data))); std::unique_ptrnode const old_head = std::move(head); head = std::move(old_head-next); return res; } void push(T new_value) { std::unique_ptrnode p(new node(std::move(new_value))); node* const new_tail = p.get(); if (tail) tail-next = std::move(p); else head = std::move(p); tail = new_tail; } }; push изменяет как tail, так и head необходимо будет защищать оба одновременно 151
  • 187. ˟̈˶́˶˵̍̂˽˶˼˻˿˸˶́˾˹̂̃̌˽˹˲˼˿˻˹́˿˳˻˱˽˹ std::shared_ptrT try_pop() { if (!head) { return std::shared_ptrT(); } std::shared_ptrT const res( std::make_sharedT(std::move(head-data))); std::unique_ptrnode const old_head = std::move(head); head = std::move(old_head-next); return res; } void push(T new_value) { std::unique_ptrnode p(new node(std::move(new_value))); node* const new_tail = p.get(); if (tail) tail-next = std::move(p); else head = std::move(p); tail = new_tail; } }; pop и push обращаются к head-next и tail-next если в очереди 1 элемент, то head-next и tail-next - один и тот же объект 152
  • 189. ˝˿˵˹̅˹̇˹́˿˳˱˾˾˱̐˳˶́̂˹̐ Head Tail Фиктивный узел ▪ При пустой очереди head и tail указывают на фиктивный узел, а не равны NULL, причём head == tail. ▪ При очереди с одним элементом head-next и tail-next указывают на разные узлы (причём head-next == tail), в результате чего гонки не возникает. 154
  • 190. ˠ̄̂̃˱̐˿̈˶́˶˵̍ Head Tail Фиктивный узел ▪ При пустой очереди head и tail указывают на фиктивный узел, а не равны NULL, причём head == tail. 155
  • 191. ˟̈˶́˶˵̍̂˿˵˾˹˽̎˼˶˽˶˾̃˿˽ Head Tail Фиктивный узел ▪ При пустой очереди head и tail указывают на фиктивный узел, а не равны NULL, причём head == tail. ▪ При очереди с одним элементом head-next и tail- next указывают на разные узлы (причём head-next == tail), в результате чего гонки не возникает. 156
  • 192. ˟̈˶́˶˵̍̂˽˶˼˻˿˸˶́˾˹̂̃̌˽˹˲˼˿˻˹́˿˳˻˱˽˹ templatetypename T class queue { private: struct node { std::shared_ptrT data; std::unique_ptrnode next; }; std::unique_ptrnode head; node *tail; public: указатель на данные вместо данных node хранит указатель на данные создание первого фиктивного узла в конструкторе queue(): head(new node), tail(head.get()) {} queue(const queue other) = delete; queue operator=(const queue other) = delete; ▪ Вводится фиктивный узел ▪ При пустой очереди head и tail теперь указывают на фиктивный узел, а не на NULL 157
  • 193. ˟̈˶́˶˵̍̂˽˶˼˻˿˸˶́˾˹̂̃̌˽˹˲˼˿˻˹́˿˳˻˱˽˹ std::shared_ptrT try_pop() { if (head.get() == tail) { return std::shared_ptrT(); } std::shared_ptrT const res(head-data); std::unique_ptrnode old_head = std::move(head); head = std::move(old_head-next); return res; } void push(T new_value) { std::shared_ptrT new_data( std::make_sharedT(std::move(new_value))); std::unique_ptrnode p(new node); tail-data = new_data; node *const new_tail = p.get(); tail-next = std::move(p); tail = new_tail; } head сравнивается с tail, а не с NULL данные извлекаются непосредственно без конструирования создание нового экземпляра T создание нового фиктивного узла записываем в старый фиктивный узел новое значение 158
  • 197. tail next data p(new node) 160
  • 199. tail next data tail-data = new_data p(new node) 161
  • 201. tail next data new_ tail next data new_tail = p.get() p(new node) 162
  • 203. tail next new_ tail tail-next = std::move(p) data next data 163
  • 205. tail next data tail = new_tail 164
  • 206. ˟̈˶́˶˵̍̂˽˶˼˻˿˸˶́˾˹̂̃̌˽˹˲˼˿˻˹́˿˳˻˱˽˹ std::shared_ptrT try_pop() { if (head.get() == tail) { return std::shared_ptrT(); } std::shared_ptrT const res(head-data); std::unique_ptrnode old_head = std::move(head); head = std::move(old_head-next); return res; } void push(T new_value) { std::shared_ptrT new_data( try_pop обращается только к head std::make_sharedT(std::move(new_value))); std::unique_ptrnode p(new node); tail-data = new_data; node *const new_tail = p.get(); tail-next = std::move(p); tail = new_tail; } обращение к tail только на момент начального сравнения push обращается только к tail 165
  • 207. ˠ˿̃˿˻˿˲˶˸˿̀˱̂˾˱̐˿̈˶́˶˵̍̂˽˶˼˻˿˸˶́˾˹̂̃̌˽˹˲˼˿˻˹́˿˳˻˱˽˹ 1 2 Head Tail ▪ Функция push обращается только к tail, try_pop - только к head (и tail на короткое время). ▪ Вместо единого глобального мьютекса можно завести два отдельных и удерживать блокировки при доступке к head и tail. 166
  • 208. ˠ˿̃˿˻˿˲˶˸˿̀˱̂˾˱̐˿̈˶́˶˵̍̂˽˶˼˻˿˸˶́˾˹̂̃̌˽˹˲˼˿˻˹́˿˳˻˱˽˹ templatetypename T class queue { private: struct node { std::shared_ptrT data; std::unique_ptrnode next; }; std::mutex head_mutex, tail_mutex; std::unique_ptrnode head; node *tail; node *get_tail() { std::lock_guardstd::mutex tail_lock(tail_mutex); return tail; } std::unique_ptrnode pop_head() { std::lock_guardstd::mutex head_lock(head_mutex); if (head.get() == get_tail()) return nullptr std::unique_ptrnode old_head = std::move(head); head = std::move(old_head-next); return old_head; } блокируется только на момент получения элемента tail 167
  • 209. ˠ˿̃˿˻˿˲˶˸˿̀˱̂˾˱̐˿̈˶́˶˵̍̂˽˶˼˻˿˸˶́˾˹̂̃̌˽˹˲˼˿˻˹́˿˳˻˱˽˹ public: threadsafe_queue(): head(new node), tail(head.get()) {} threadsafe_queue(const threadsafe_queue other) = delete; threadsafe_queue operator=(const threadsafe_queue other)=delete; std::shared_ptrT try_pop() { std::unique_ptrnode old_head = pop_head(); return old_head ? old_head-data : std::shared_ptrT(); } void push(T new_value) { std::shared_ptrT new_data( std::make_sharedT(std::move(new_value))); std::unique_ptrnode p(new node); node* const new_tail = p.get(); std::lock_guardstd::mutex tail_lock(tail_mutex); tail-data = new_data; tail-next = std::move(p); tail = new_tail; } }; push обращается только к tail, но не к head, поэтому используется одна блокировка 168
  • 210. ˠ˿̃˿˻˿˲˶˸˿̀˱̂˾˱̐˿̈˶́˶˵̍̂˽˶˼˻˿˸˶́˾˹̂̃̌˽˹˲˼˿˻˹́˿˳˻˱˽˹ public: threadsafe_queue(): head(new node), tail(head.get()) {} threadsafe_queue(const threadsafe_queue other) = delete; threadsafe_queue operator=(const threadsafe_queue other)=delete; std::shared_ptrT try_pop() { std::unique_ptrnode old_head = pop_head(); return old_head ? old_head-data : std::shared_ptrT(); } void push(T new_value) { node *const old_tail = get_tail(); std::lock_guardstd::mutex head_lock(head_mutex); if (head.get() == old_tail) { return nullptr; } std::unique_ptrnode old_head = std::move(head); head = std::move(old_head-next); return old_head; } }; выполняется не под защитой мьютекса head_mutex 169
  • 211. ˠ˿̃˿˻˿˲˶˸˿̀˱̂˾˱̐˿̈˶́˶˵̍̂˽˶˼˻˿˸˶́˾˹̂̃̌˽˹˲˼˿˻˹́˿˳˻˱˽˹ public: threadsafe_queue(): head(new node), tail(head.get()) {} threadsafe_queue(const threadsafe_queue other) = delete; threadsafe_queue operator=(const threadsafe_queue other)=delete; std::shared_ptrT try_pop() { std::unique_ptrnode old_head = pop_head(); return old_head ? old_head-data : std::shared_ptrT(); } void push(T new_value) { node *const old_tail = get_tail(); std::lock_guardstd::mutex head_lock(head_mutex); if (head.get() == old_tail) { return nullptr; } std::unique_ptrnode old_head = std::move(head); head = std::move(old_head-next); return old_head; } }; выполняется не под защитой мьютекса head_mutex 170
  • 212. ˠ˿̃˿˻˿˲˶˸˿̀˱̂˾˱̐˿̈˶́˶˵̍̂˽˶˼˻˿˸˶́˾˹̂̃̌˽˹˲˼˿˻˹́˿˳˻˱˽˹˹̂˿˷˹˵˱˾˹˶˽ ̀˿̂̃̄̀˼˶˾˹̐̎˼˶˽˶˾̃˱ Особенности реализации: ▪ Освободить мьютекс в push до вызова notify_one, чтобы разбуженный поток не ждал освобождения мьютекса. ▪ Проверку условия можно выполнять под защитой head_mutex, захватывая tail_mutex только для чтения tail. Предикат выглядит как head != get_tail() ▪ Для версии pop, работающей со ссылкой, необходимо переопределить wait_and_pop(), чтобы обеспечить безопасность с точки зрения исключений. Необходимо сначала скопировать данные из узла, а потом удалять узел из списка. 171
  • 213. ˠ˿̃˿˻˿˲˶˸˿̀˱̂˾˱̐˿̈˶́˶˵̍̂˽˶˼˻˿˸˶́˾˹̂̃̌˽˹˲˼˿˻˹́˿˳˻˱˽˹˹̂˿˷˹˵˱˾˹˶˽ ̀˿̂̃̄̀˼˶˾˹̐̎˼˶˽˶˾̃˱˿˲̋̐˳˼˶˾˹˶˻˼˱̂̂˱ templatetypename T class queue { private: struct node { std::shared_ptrT data; std::uniquet_ptrnode next; }; std::mutex head_mutex, tail_mutex; std::unique_ptrnode head; node *tail; std::condition_variable data_cond; public: threadsafe_queue(): head(new node), tail(head.get()) {} threadsafe_queue(const threadsafe_queue other) = delete; std::shared_ptrT try_pop(); bool try_pop(T value); std::shared_ptrT wait_and_pop(); void wait_and_pop(T value); void push(T new_value); void empty(); }; 172
  • 214. ˠ˿̃˿˻˿˲˶˸˿̀˱̂˾˱̐˿̈˶́˶˵̍̂˽˶˼˻˿˸˶́˾˹̂̃̌˽˹˲˼˿˻˹́˿˳˻˱˽˹˹̂˿˷˹˵˱˾˹˶˽ ̀˿̂̃̄̀˼˶˾˹̐̎˼˶˽˶˾̃˱˵˿˲˱˳˼˶˾˹˶˾˿˳̌̆˸˾˱̈˶˾˹˺ templatetypename T void threadsafe_queueT::push(T new_value) { std::shared_ptrT new_data( std::make_sharedT(std::move(new_value))); std::unique_ptrnode p(new node); { std::lock_guardstd::mutex tail_lock(tail_mutex); tail-data = new_data; node* const new_tail = p.get(); tail-next = std::move(p); tail = new_tail; } data_cond.notify_one(); } 173
  • 215. ˠ˿̃˿˻˿˲˶˸˿̀˱̂˾˱̐˿̈˶́˶˵̍̂˽˶˼˻˿˸˶́˾˹̂̃̌˽˹˲˼˿˻˹́˿˳˻˱˽˹˹̂˿˷˹˵˱˾˹˶˽ ̀˿̂̃̄̀˼˶˾˹̐̎˼˶˽˶˾̃˱˿˷˹˵˶˾˹˶˹˹˸˳˼˶̈˶˾˹˶̎˼˶˽˶˾̃˱ templatetypename T class threadsafe_queue { private: node* get_tail() { std::lock_guardstd::mutex tail_lock(tail_mutex); return tail; } std::unique_ptrnode pop_head() { std::unique_ptrnode old_head = std::move(head); head = std::move(old_head-next); return old_head; } std::unique_lockstd::mutex wait_for_data() { std::unique_lockstd::mutex head_lock(head_mutex); data_cond.wait(head_lock, []{return head.get() != get_tail(); }); return std::move(head_lock); } Модификация списка в результате удаления головного элемента. Ожидание появления данных в Возврат объекта блокировки очереди 174
  • 216. ˠ˿̃˿˻˿˲˶˸˿̀˱̂˾˱̐˿̈˶́˶˵̍̂˽˶˼˻˿˸˶́˾˹̂̃̌˽˹˲˼˿˻˹́˿˳˻˱˽˹˹̂˿˷˹˵˱˾˹˶˽ ̀˿̂̃̄̀˼˶˾˹̐̎˼˶˽˶˾̃˱˿˷˹˵˶˾˹˶˹˹˸˳˼˶̈˶˾˹˶̎˼˶˽˶˾̃˱ std::unique_ptrnode wait_pop_head() { std::unique_lockstd::mutex head_lock(wait_for_data()); return pop_head(); } std::unique_ptrnode wait_pop_head(T value) { std::unique_lockstd::mutex head_lock(wait_for_data()); value = std::move(*head-data); return pop_head(); } public: std::shared_ptrT wait_and_pop() { std::unique_ptrnode const old_head = wait_pop_head(); return old_head-data; } void wait_and_pop(T value) { std::unique_ptrnode const old_head = wait_pop_head(value); }}; Модификация данных под защитой мьютекса, захваченного в wait_for_data Модификация данных под защитой мьютекса, захваченного в wait_for_data 175
  • 218. ˹HPSW
  • 219. private: std::unique_ptrnode try_pop_head() { std::lock_guardstd::mutex head_lock(head_mutex); if (head.get() == get_tail()) { return std::unique_ptrnode(); } return pop_head(); } std::unique_ptrnode try_pop_head(T value) { std::lock_guardstd::mutex head_lock(head_mutex); if (head.get() == get_tail()) { return std::unique_ptrnode(); } value = std::move(*head-data); return pop_head(); } 176
  • 221. ˹HPSW
  • 222. public: std::shared_ptrT try_pop() { std::unique_ptrnode old_head = try_pop_head(); return old_head ? old_head-data : std::shared_ptrT(); } bool try_pop(T value) { std::unique_ptrnode const old_head = try_pop_head(value); return old_head; } void empty() { std::lock_guardstd::mutex head_lock(head_mutex); return (head.get() == get_tail()); } }; 177