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

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

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

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