C++: параллельность, асинхронность, атомарность

Вопросы написания собственного программного кода (на любых языках)

Модератор: Olej

Аватара пользователя
Olej
Писатель
Сообщения: 14939
Зарегистрирован: 24 сен 2011, 14:22
Откуда: Харьков
Контактная информация:

Re: C++: параллельность, асинхронность, атомарность

Непрочитанное сообщение Olej » 06 мар 2021, 03:36

Olej писал(а):
05 мар 2021, 06:05
интересный цикл статей (штук 11
Lock-free структуры данных. Основы: Модель памяти
Барьеры компилятора
Кто может переупорядочить код, написанный нами? Мы выяснили, что это может делать процессор. Но есть ещё один источник переупорядочения – компилятор.
...
Для GCC и Clang – это изящная конструкция
__asm__ __volatile__ ( "" ::: "memory" )

Надо отметить, что ассемблерные вставки __asm__ __volatile__ ( … ) также являются в некотором роде барьером для GCC/Clang: компилятор не имеет права ни выкидывать, ни перемещать их вверх/вниз по коду (об этом говорит модификатор __volatile__).
Константы memory_order воздействуют на компилятор, поддерживающий C++11, в той же мере, как и на процессор, — они являются барьером компилятора, ограничивая возможности компилятора по переупорядочению (то есть оптимизации) нашего кода. Поэтому указание особых барьеров компилятора не требуется, если, конечно, компилятор полностью поддерживает новый стандарт.

Аватара пользователя
Olej
Писатель
Сообщения: 14939
Зарегистрирован: 24 сен 2011, 14:22
Откуда: Харьков
Контактная информация:

Re: C++: параллельность, асинхронность, атомарность

Непрочитанное сообщение Olej » 06 мар 2021, 04:46

Отвлекаясь от предметного разговора:
Энтони Уильямс, Параллельное программирование на С++ в действии
Изображение
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

Аватара пользователя
Olej
Писатель
Сообщения: 14939
Зарегистрирован: 24 сен 2011, 14:22
Откуда: Харьков
Контактная информация:

Re: C++: параллельность, асинхронность, атомарность

Непрочитанное сообщение Olej » 07 мар 2021, 10:42

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();
}
Там нет ничего интересного, можно не вникать :lol: , кроме:
- в создании потока указывается метод класса X - &X::do_lengthy_work ...
- это делается для разных экземпляров X, и 2-м параметром создания потока обязательно указывается ссылка на сам объект для которого ... это фактически *this :-o
- и только затем идут сами параметры вызываемого метода-функции...

Такое не всегда прийдёт в голову когда оно понадобится...
А оно надобится! ;-)

Код: Выделить всё

olej@nvme:~/2021/OWN_TEST.codes/asynch$ ./thread2
создание потоков, медленно считающих ...
поток #0 завершился
поток #1 завершился
поток #2 завершился
поток #3 завершился
Вложения
thread2.cc
(955 байт) 18 скачиваний

Аватара пользователя
Olej
Писатель
Сообщения: 14939
Зарегистрирован: 24 сен 2011, 14:22
Откуда: Харьков
Контактная информация:

Re: C++: параллельность, асинхронность, атомарность

Непрочитанное сообщение Olej » 07 мар 2021, 19:19

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.

Аватара пользователя
Olej
Писатель
Сообщения: 14939
Зарегистрирован: 24 сен 2011, 14:22
Откуда: Харьков
Контактная информация:

Re: C++: параллельность, асинхронность, атомарность

Непрочитанное сообщение Olej » 14 май 2021, 12:27

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 
поток запущен...
поток завершется...
программа завершется
Вложения
thread_lambda.cc
(465 байт) 10 скачиваний

Аватара пользователя
Olej
Писатель
Сообщения: 14939
Зарегистрирован: 24 сен 2011, 14:22
Откуда: Харьков
Контактная информация:

Re: C++: параллельность, асинхронность, атомарность

Непрочитанное сообщение Olej » 14 май 2021, 12:37

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-мя потоками ядра практически не будут происходить (не считая редких переконфигураций корутин между потоками);

Аватара пользователя
Olej
Писатель
Сообщения: 14939
Зарегистрирован: 24 сен 2011, 14:22
Откуда: Харьков
Контактная информация:

Re: C++: параллельность, асинхронность, атомарность

Непрочитанное сообщение Olej » 14 май 2021, 14:26

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 завершется...
программа завершется
Вложения
thread_lambda_m.cc
(1.06 КБ) 10 скачиваний

Аватара пользователя
Olej
Писатель
Сообщения: 14939
Зарегистрирован: 24 сен 2011, 14:22
Откуда: Харьков
Контактная информация:

Re: C++: параллельность, асинхронность, атомарность

Непрочитанное сообщение Olej » 14 май 2021, 17:26

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 мсек.
программа завершается

Ответить

Вернуться в «Программирование»

Кто сейчас на конференции

Сейчас этот форум просматривают: нет зарегистрированных пользователей и 3 гостя