Страница 2 из 2
Re: C++: параллельность, асинхронность, атомарность
Добавлено: 06 мар 2021, 03:36
Olej
Olej писал(а): ↑05 мар 2021, 06:05
интересный цикл статей (штук 11
Lock-free структуры данных. Основы: Модель памяти
Барьеры компилятора
Кто может переупорядочить код, написанный нами? Мы выяснили, что это может делать процессор. Но есть ещё один источник переупорядочения – компилятор.
...
Для GCC и Clang – это изящная конструкция
__asm__ __volatile__ ( "" ::: "memory" )
Надо отметить, что ассемблерные вставки __asm__ __volatile__ ( … ) также являются в некотором роде барьером для GCC/Clang: компилятор не имеет права ни выкидывать, ни перемещать их вверх/вниз по коду (об этом говорит модификатор __volatile__).
Константы memory_order воздействуют на компилятор, поддерживающий C++11, в той же мере, как и на процессор, — они являются барьером компилятора, ограничивая возможности компилятора по переупорядочению (то есть оптимизации) нашего кода. Поэтому указание особых барьеров компилятора не требуется, если, конечно, компилятор полностью поддерживает новый стандарт.
Re: C++: параллельность, асинхронность, атомарность
Добавлено: 06 мар 2021, 04:46
Olej
Отвлекаясь от предметного разговора:
Энтони Уильямс, Параллельное программирование на С++ в действии
ISBN: 978-5-97060-194-5
672 страницы
январь 2016
ДМК Пресс
Более раннее издание
свободно качаем здесь:
Параллельное программирование на C++ в действии. Практика разработки многопоточных программ
М.: ДМК Пресс, 2012. – 672с.
Учитывая "удивительное совпадение" числа страниц, не думаю что там есть какая-то разница.
Код: Выделить всё
olej@nvme:~/Загрузки$ l -l Параллельное\ программирование\ на\ С++\ в\ действии.\ Практика\ разработки\ многопоточных\ программ.\ Энтони\ Уильямс
-rw-rw-r-- 1 olej olej 2945342 мар 6 03:33 'Параллельное программирование на С++ в действии. Практика разработки многопоточных программ. Энтони Уильямс'
Код: Выделить всё
olej@nvme:~/Загрузки$ file Параллельное\ программирование\ на\ С++\ в\ действии.\ Практика\ разработки\ многопоточных\ программ.\ Энтони\ Уильямс
Параллельное программирование на С++ в действии. Практика разработки многопоточных программ. Энтони Уильямс: PDF document, version 1.6
Re: C++: параллельность, асинхронность, атомарность
Добавлено: 07 мар 2021, 10:42
Olej
Olej писал(а): ↑20 фев 2021, 15:12
В отношении самих потоков в C++, по идее, не должно возникать никаких вопросов? ...
Olej писал(а): ↑06 мар 2021, 04:46
Отвлекаясь от предметного разговора:
Энтони Уильямс, Параллельное программирование на С++ в действии
Интересный примерчик по мотивам книжули (см. там стр. 54) - как в качестве функции потока вызвать
метод класса + передать ему его параметры:
Код: Выделить всё
#include <iostream>
#include <atomic> // std::atomic, std::atomic_flag, ATOMIC_FLAG_INIT
#include <thread> // std::thread, std::this_thread::yield
#include <vector> // std::vector
#include <unistd.h>
std::atomic<bool> ready (false);
class X
{
public:
void do_lengthy_work( int tid, long int limit )
{
while (!ready) { std::this_thread::yield(); } // wait for the ready signal
do usleep( 1 );
while (limit--);
std::cout << "поток #" << tid << " завершился" << std::endl;
};
};
int main ()
{
std::vector<X> objs = { X(), X(), X(), X() };
std::cout << "создание потоков, медленно считающих ..." << std::endl;
std::vector<std::thread> threads;
for (long unsigned int i = 0; i < objs.size(); ++i)
{
long int len = 10000 * (i + 1);
threads.push_back(std::thread( &X::do_lengthy_work, &objs[ i ], i, len ) );
}
ready = true;
for (auto& th : threads) th.join();
}
Там нет ничего интересного, можно не вникать
, кроме:
- в создании потока указывается
метод класса X - &X::do_lengthy_work ...
- это делается для разных экземпляров X, и 2-м параметром создания потока
обязательно указывается ссылка на сам объект для которого ... это фактически *this
- и только
затем идут сами параметры вызываемого метода-функции...
Такое не всегда прийдёт в голову когда оно понадобится...
А оно надобится!
Код: Выделить всё
olej@nvme:~/2021/OWN_TEST.codes/asynch$ ./thread2
создание потоков, медленно считающих ...
поток #0 завершился
поток #1 завершился
поток #2 завершился
поток #3 завершился
Re: C++: параллельность, асинхронность, атомарность
Добавлено: 07 мар 2021, 19:19
Olej
Olej писал(а): ↑22 фев 2021, 02:40
как я вспомнил ... кстати - я ещё в 2014г. написал рукопись-черновик книги "Параллелизм, конкурентность, многопроцессорность в Linux"
Ну и конечно, вот это:
Olej писал(а): ↑17 янв 2013, 22:11
QNX/UNIX: анатомия параллелизма.
Вот здесь можете эту книжку свободно скачать.
Вот моя книга, 2005 г., изданная в Санкт-Петербурге:
Олег Цилюрик, Егор Горошко
QNX/UNIX: анатомия параллелизма
ISBN: 5-93286-088-X
288 страниц
декабрь 2005
Символ-Плюс
О механизмах параллельности POSIX API и расширениях реального времени POSIX 1003d.
Re: C++: параллельность, асинхронность, атомарность
Добавлено: 14 май 2021, 12:27
Olej
Olej писал(а): ↑07 мар 2021, 10:42
Такое не всегда прийдёт в голову когда оно понадобится...
В связи с введением в стандарте C++11 анонимных lambda-функций можно (а у многих молодых программеров
стало модным) писать код потока как-то так:
Код: Выделить всё
#include <iostream>
#include <thread>
int main ()
{
auto concurrent = std::thread(
[]{
std::this_thread::sleep_for(std::chrono::milliseconds(500)); // do something ...
int sum = 0;
for (int i = 0; i < 2000; ++i)
sum += i;
std::cout << "поток завершется..." << std::endl;
}
);
std::cout << "поток запущен..." << std::endl;
concurrent.join();
std::cout << "программа завершется" << std::endl;
}
Код: Выделить всё
olej@R420:~/2021/OWN_TEST.codes/thread$ make
g++ -Wall -pthread -pedantic -std=c++14 -pthread thread_lambda.cc -o thread_lambda
Код: Выделить всё
olej@R420:~/2021/OWN_TEST.codes/thread$ ./thread_lambda
поток запущен...
поток завершется...
программа завершется
Re: C++: параллельность, асинхронность, атомарность
Добавлено: 14 май 2021, 12:37
Olej
Olej писал(а): ↑14 май 2021, 12:27
В связи с введением в стандарте C++11 анонимных lambda-функций можно (а у многих молодых программеров стало модным) писать код потока как-то так:
Это сильно похоже на параллельные фрагменты кода в языке Go (Go-рутины) ... но, на самом деле, принципиально отличается от...
1. в C++ при такой манере записи 100 потоков будет создано 100 thread_t ядра ... на компьютере с 4 процессорами (ядрами) 4 их этих потоков будут (в любой момент времени) выполняться, а остальные 96 будут находиться в блокированном состоянии ... переключение между 100 потоками - довольно трудоёмко (вызов API ядра);
2. в Go на 4 процессорном компьютере будет создано 4 thread_t ядра, между которыми будет распределены примерно поровну (по 25) корутин пространства пользователя (из общего числа 100) ... передача управления между корутинами в пределах одного thread_t будет происходить кооперативным образом, в пространстве пользователя (без вмешательства ядра), в 2-3 машинных команды; переключений между 4-мя потоками ядра практически не будут происходить (не считая редких переконфигураций корутин между потоками);
Re: C++: параллельность, асинхронность, атомарность
Добавлено: 14 май 2021, 14:26
Olej
Olej писал(а): ↑14 май 2021, 12:27
В связи с введением в стандарте C++11 анонимных lambda-функций можно (а у многих молодых программеров стало модным) писать код потока как-то так:
Или даже так, когда нам нужна целая куча потоков...
(кстати, как-раз очень частая ситуация, когда нужны N
эквивалентных потоков, с одинаковой функцией потока, но, возможно, с разными параметрами запуска)
Код: Выделить всё
#include <iostream>
#include <sstream>
#include <atomic> // std::atomic, std::atomic_flag, ATOMIC_FLAG_INIT
#include <thread> // std::thread, std::this_thread::yield
std::atomic<bool> ready(false);
int main ()
{
const int num = 5;
std::thread threads[ num ];
for (int i = 0; i < num; ++i)
{
threads[i] = std::thread(
[](int n){
while (!ready) { std::this_thread::yield(); } // wait for the ready signal
std::this_thread::sleep_for(std::chrono::milliseconds(300)); // do something ...
std::stringstream ss;
ss << "поток " << n << " завершется..." << std::endl;
std::cout << ss.str();
}, i // thread start parameters
);
std::cout << "поток " << i << " запущен..." << std::endl;
}
ready = true; // simultaneous start of threads
for (int i = 0; i < num; ++i)
threads[i].join();
std::cout << "программа завершется" << std::endl;
}
Код: Выделить всё
olej@R420:~/2021/OWN_TEST.codes/thread$ make
g++ -Wall -pthread -pedantic -std=c++14 -pthread thread_lambda_m.cc -o thread_lambda_m
Код: Выделить всё
olej@R420:~/2021/OWN_TEST.codes/thread$ ./thread_lambda_m
поток 0 запущен...
поток 1 запущен...
поток 2 запущен...
поток 3 запущен...
поток 4 запущен...
поток 3 завершется...
поток 2 завершется...
поток 0 завершется...
поток 1 завершется...
поток 4 завершется...
программа завершется
Re: C++: параллельность, асинхронность, атомарность
Добавлено: 20 ноя 2023, 18:23
Olej
Olej писал(а): ↑14 май 2021, 14:26
Или даже так,
Или даже так:
Код: Выделить всё
#include <iostream>
#include <sstream>
#include <atomic> // std::atomic, std::atomic_flag, ATOMIC_FLAG_INIT
#include <thread> // std::thread, std::this_thread::yield
#include <chrono>
#include <random>
std::atomic<bool> ready(false);
int main ()
{
unsigned seed = std::chrono::system_clock::now().time_since_epoch().count();
std::minstd_rand generator(seed); // minstd_rand is a standard linear_congruential_engine
const long unsigned int divider = generator.max() / 10;
const int num = 5;
std::thread threads[ num ];
for (int i = 0; i < num; ++i)
{
int delay = (generator() / divider + generator.min()) * 200; // duration for next thread
threads[i] = std::thread(
[](int n, int d){
while (!ready) { std::this_thread::yield(); } // wait for the ready signal
std::this_thread::sleep_for(std::chrono::milliseconds(d)); // do something ...
std::stringstream ss;
ss << "поток " << n << " завершился через " << d << " мсек." << std::endl;
std::cout << ss.str();
}, i, delay // thread start parameters
);
std::cout << "поток " << i << " запущен..." << std::endl;
}
ready = true; // simultaneous start of threads
for (int i = 0; i < num; ++i)
threads[i].join();
std::cout << "программа завершается" << std::endl;
}
Код: Выделить всё
olej@R420:~/2021/OWN_TEST.codes/thread$ make
g++ -Wall -pthread -pedantic -std=c++14 -pthread thread_lambda_r.cc -o thread_lambda_r
... когда хорошо видно случайное чередование завершения одновременно запускаемых и параллельно выполняемых потоков (как меняется очерёдность их завершения):
Код: Выделить всё
olej@R420:~/2021/OWN_TEST.codes/thread$ ./thread_lambda_r
поток 0 запущен...
поток 1 запущен...
поток 2 запущен...
поток 3 запущен...
поток 4 запущен...
поток 4 завершился через 400 мсек.
поток 2 завершился через 600 мсек.
поток 1 завершился через 800 мсек.
поток 0 завершился через 1800 мсек.
поток 3 завершился через 2000 мсек.
программа завершается
Код: Выделить всё
olej@R420:~/2021/OWN_TEST.codes/thread$ ./thread_lambda_r
поток 0 запущен...
поток 1 запущен...
поток 2 запущен...
поток 3 запущен...
поток 4 запущен...
поток 2 завершился через 200 мсек.
поток 4 завершился через 600 мсек.
поток 1 завершился через 800 мсек.
поток 0 завершился через 1600 мсек.
поток 3 завершился через 1800 мсек.
программа завершается
Код: Выделить всё
olej@R420:~/2021/OWN_TEST.codes/thread$ ./thread_lambda_r
поток 0 запущен...
поток 1 запущен...
поток 2 запущен...
поток 3 запущен...
поток 4 запущен...
поток 0 завершился через 400 мсек.
поток 1 завершился через 400 мсек.
поток 4 завершился через 1000 мсек.
поток 2 завершился через 1200 мсек.
поток 3 завершился через 2000 мсек.
программа завершается