LINUX.ORG.RU

Вернуть результат thread в С++ без мутексов?

 ,


0

1

Можно так?

Логика - запускаю thread.detach(), который либо после проверок сразу завершается, если проверки пройдены встает на ожидание внешнего события (слушает на сокете порт), как только событие наступает делает некоторые действия и завершается - так вот из основного потока хотелось бы знать выполняется ли еще этот поток, если нет, то получить из него значение БЕЗ блокировки основного потока?

Показалось future с его async() подойдет, но оказалось valid() проверяет всего лишь был ли async() и не делали ли еще get(), а не отсутствие результата для get(), следовательно если поток не завершился get() блокирует основной поток.

Не то чтобы мутексы проблема использовать, но может проще можно как то для одного единственного простого значения состояния?

★★★

Ответ на: комментарий от Silerus

единственно там написано если я правильно шпрехаю

A thread that has finished executing code, but has not yet been joined is still considered an active thread of execution and is therefore joinable.

так если я сделаю join не завершенного потока из основного - он опять встанет на блокировку

т.е. joinable() показывает можно ли присоединить поток, а не завершился ли он, естественно после detach() присоединиться нельзя, но это не означает что поток завершился.

wolverin ★★★
() автор топика
Последнее исправление: wolverin (всего исправлений: 2)
Ответ на: комментарий от wolverin

хочу уточнить, что основной поток не опрашивает выделенный в цикле, а движется последовательно по своим задачам, конечная точка в которой уже надо точно знать чем там все завершилось или ждать конечно есть, но хотелось бы по ходу основного потока тоже проверять, чтобы не выполнять дополнительной работы, а просто завершиться.

wolverin ★★★
() автор топика
Последнее исправление: wolverin (всего исправлений: 1)
Ответ на: комментарий от Silerus

надо проверить конечно, но судя по тексту как я понял еще раз - joinable() не определяет завершен ли поток, только возможность сделать его join()

wolverin ★★★
() автор топика
Ответ на: комментарий от ox55ff

Чем это должно помочь? Так я основной поток завершал если там проблемы какие то, теперь нужно будет как то завершить то, что где то в чужой библиотеке ждет.

wolverin ★★★
() автор топика
Ответ на: комментарий от wolverin

Ладно, если тебя устраивает ситуация, когда поток гуляет сам по себе, то делай как хочешь.

так вот из основного потока хотелось бы знать выполняется ли еще этот поток, если нет, то получить из него значение БЕЗ блокировки основного потока?

Если тебе флаг нужно передать, то можно использовать атомарную переменную std::atomic_bool flag. Флаг просто устанавливай при завершении потока. Проверять можно откуда угодно.

Если нужно вернуть сложный объект, то std::promise и std::future. Фьючу можно ждать как в блокирующем режиме, так и в неблокирующем.

ox55ff ★★★★★
()
Ответ на: комментарий от wolverin

eao197 уже дал ссылку на метод wait_for https://en.cppreference.com/w/cpp/thread/future/wait_for

Ему можно передать время в течение которого будет ждать. Просто передавай маленькое время и проверяй статус, который тебе вернёт этот метод.

ox55ff ★★★★★
()

детач сделан для того, чтобы некий тред запускал один или несколько других тредов и помирал(например). то есть запущенные треды живут дальше, а тот, кто их запускал, не заботится об их существовании.

ты же делаешь детач, и зачем-то спрашиваешь - жив ли отсоединенный тред. то есть поступаешь вопреки логике detach.

если таки такое поведение нужно, то детачнутый тред должен взводить флаг, который мастерный тред например опрашивает. или какой-то обьект синхронизации, на котором можно делать ожидание… но ты таким образом эмулируешь поведение недетачнутого треда. единственно что тут можно сделать какой-то таймаут ожидания завершения.

опять же надо понимать, что если тред взвел флаг - значит он еще работает. и мастерный тред не может увидя взведенный флаг каким-то образом прибивать ресурсы того, кто флаг взводит.

то есть флаг говорит о том, что тред близок к завершению, но не завершился.

alysnix ★★★
()
Последнее исправление: alysnix (всего исправлений: 1)
Ответ на: комментарий от alysnix

а из основного почему нельзя запустить треды то эти?

я делаю детач, потому что мне без разницы чо там внутри происходит, но хочу знать результат не в точке когда у меня куча ресурсов проинициализируется, а переодически смотреть пораньше, но если в процессе инициализации что то пошло не так, просто завершиться не заморачиваясь что там сторонняя библиотека ждет события, т.е. завершение основного потока это все, алярма, ничего больше ждать не надо, поскольку основной поток должен работать БЕСКОНЕЧНОЕ время в идеале

попробую wait_for, хотя это по факту и не неблокирующий режим…

wolverin ★★★
() автор топика

Имхо ты всё как-то переусложняешь и велосипедишь там, где не надо.

Возьми каналы (boost::fiber::unbounded_channel, например, или любую другую реализацию) и передавай сообщение по каналу. Детач, соответственно, убери.

Проверка наличия данных в канале может быть не блокирующей. Простая и понятная схема обмена без дичи и без потенциальных багов.

Ivan_qrt ★★★★★
()
Ответ на: комментарий от Ivan_qrt

нечему в этом левом потоке у меня в каналы слать, по сути сторонняя либа там инициализирует большой ресурс, есть начало и конец, периодически хочется знать не пришел ли так конец не блокируя ничего ) причем до того момента, когда уже точно надо ждать конца того потока, при этом НИГДЕ никаких циклов в моем коде нет на этом участке.

wolverin ★★★
() автор топика
Последнее исправление: wolverin (всего исправлений: 1)
Ответ на: комментарий от wolverin

Ну так оберни функцию, которую ты передаёшь в конструктор треда в лямбду и перед выходом из лямбды отправляй сообщение.

Тред же ты запускаешь, а не библиотечная функция?

Ivan_qrt ★★★★★
()
Ответ на: комментарий от wolverin

а из основного почему нельзя запустить треды то эти?

можно. в таком случае стратегия - запустил и забыл. но поскольку тебе все равно основной надо завершить после чайлдов, то и детачить и смысла нет.

alysnix ★★★
()
Ответ на: комментарий от Ivan_qrt

Ну так оберни функцию, которую ты передаёшь в конструктор треда в лямбду и перед выходом из лямбды отправляй сообщение.

и получит он сообщение, что тред сконструировался. а не завершился.

alysnix ★★★
()
Ответ на: комментарий от wolverin

я так jobы перебираю, хз что там у вас блокируется…

if(job.wait_for(std::chrono::milliseconds(1)) != std::future_status::ready)
    continue;

detach нужен там где ты точно знаешь что время жизни потока меньше главного и не нужен результат.

если async перебор добавь std::atomic«int» и пиши там свой прогресс

anonymous2 ★★★★★
()
Последнее исправление: anonymous2 (всего исправлений: 6)

Понапишут лапши на футурах, а потом в жопу долбяс отладчиком маются. Просто жди результата по таймауту из той же футуры, а лучше подумай над циклом обработки событий, если хочешь в хоть какую-то эффективность.

faq2
()

получить из него значение БЕЗ блокировки основного потока?

Это целый ворох проблем: lock-free алгоритмы. Конкретнее в твоём случае — lock-free очередь. Но скорее всего, тебе это не нужно, и достаточно будет блокирующей очереди.

i-rinat ★★★★★
()
#include <memory>
#include <atomic>
#include <thread>
#include <vector>
using namespace std;


struct ITC_mes {
	atomic_flag f;
	int data;
};
using arg_t = shared_ptr<ITC_mes>;

void thread_fn(arg_t a) {
	a->data = 5;
	a->f.test_and_set(memory_order_release);
}

int main() {
	vector<arg_t> mes{make_shared<ITC_mes>()};
	thread t(thread_fn, mes.back());
	t.detach();
	while (true) {
		if (mes[0]->f.test(memory_order_acquire))
			return mes[0]->data;
	}
}

kvpfs ★★
()
Последнее исправление: kvpfs (всего исправлений: 1)
Ответ на: комментарий от alysnix

и получит он сообщение, что тред сконструировался. а не завершился.

Чёт я не догоняю, почему. Я может условия как-то не правильно понял, но судя по описанию у ОПа есть функция инициализации, сделанная сторонними разработчиками. Тред с ней он запускает сам.

Так в чём проблема сделать

thread([&chan]() { init_func(); chan.write("finishded"); });

Ну и да, если нужно только передать сигнал о завершении, то атомика достаточно. Я ориентировался на то, что нужно результат вернуть.

Ivan_qrt ★★★★★
()
Ответ на: комментарий от Ivan_qrt

Не только результат, его можно просто через join ждать, хочу по ходу основного потока проверять нет ли уже результата максимально просто без блокировок

wolverin ★★★
() автор топика
Ответ на: комментарий от anonymous2

Да на этом видимо и придется остановиться, просто с этим вайтом не нравится 2 вещи

  1. Это блокировка основного потока по определению, а мне нужно избавиться максимально от всех простоев в потоках тем более по времени
  2. Придется подбирать время, в моем случае участка в сторонней либе, когда прошла там инициализация и встало на ожидание внешнего события, чтобы узнать не выстрелила ли там ошибка

И да, время жизни этого потока точно меньше основного

wolverin ★★★
() автор топика
Последнее исправление: wolverin (всего исправлений: 2)
Ответ на: комментарий от wolverin

Не то чтобы основная проблема в этой именно задаче, просто хотелось бы разобраться как максимально эффективно решать подобные проблемы, поскольку после инициализации мне нужно будет так прикидываю создать 4 потока

  1. Основной читает с камеры и является самым быстрым
  2. Поток внешнего rtsp сервера, который при подключении клиента должен запускать ранее созданные потоки 3 и 4
  3. Декодирование
  4. Кодирование и запись в сетевой буфер (чтобы обмениваться с п.2 не через файловый буфер, тк сторонняя либа)
  5. Запись на диск некодированного потока из п.1

А потом ещё и поток mqtt клиента прикрутить, чтобы ещё в одном потоке минуя п.2 слать видео прям на внешнего клиента видео записанное на диске ))) а ядра всего 4…

wolverin ★★★
() автор топика
Последнее исправление: wolverin (всего исправлений: 1)
Ответ на: комментарий от wolverin

Это блокировка основного потока по определению, а мне нужно избавиться максимально от всех простоев в потоках тем более по времени

Придется подбирать время, в моем случае участка в сторонней либе, когда прошла там инициализация и встало на ожидание внешнего события, чтобы узнать не выстрелила ли там ошибка

У меня есть ощущение, что вам ошибка в ДНК мешает набросать на коленке что-то вроде вот такого:

#include <future>
#include <thread>
#include <iostream>
#include <chrono>

using namespace std::chrono_literals;

int main()
{
	std::promise<int> promise;
	auto ft = promise.get_future();

	std::thread worker{
		[&promise]() {
			std::this_thread::sleep_for(250ms);
			promise.set_value(42);
		}
	};

	while( ft.wait_for(0s) == std::future_status::timeout )
	{
		std::cout << "result isn't ready yet" << std::endl;
		std::this_thread::sleep_for(50ms);
	}

	std::cout << "result is: " << ft.get() << std::endl;
	worker.join();
}

и снять все дурацкие вопросы. Вместо этого вы тратите гораздо больше времени на написание всякой фигни в этой теме.

eao197 ★★★★★
()
Ответ на: комментарий от eao197

Ещё можно заменить std::thread на std::jthread. Как сказал Nikolai Jossutis, jthread - это то, чем должен был быть thread (если бы его сделали правильно).

Кстати, в коде ошибка: если до worker.join() будет выброшенно исключение, то worker не будет «joined», и его деструктор приведет к std::terminate.

rupert ★★★★★
()
Ответ на: комментарий от rupert

Я не знаю каким стандартом ограничен ТС, а std::jthread – это вроде как C++20, который лично мне пока что недоступен.

Ну и это не промышленный код, а минутный набросок для того, чтобы проверить, что 0 для std::future::wait_for – это допустимое значение. Здесь вообще никаких исключений не обрабатывается и намеренно, дабы не усложнять и не прятать смысл происходящего.

eao197 ★★★★★
()

Можно использовать std::atomic_flag, его можно ставить и проверять без mutex.

Можно его взвести после того как рабочий поток закончил работу и сложил все результаты куда-то и гарантированно больше не будет их трогать. Соответственно, ты из основного потока можешь их забрать после успешной проверки флага, не блокируя ничего, ведь твой флаг уже гарантирует синхронизацию.

Флаги реализованы на специальных инструкциях процессора и работают быстрее любых системных вызовов.

KivApple ★★★★★
()
Ответ на: комментарий от eao197

Ждать по времени имхо ошибка в ДНК, пока физически не успел проверить с этими тупыми вайтами, но может из своего опыта кто подскажет и более разумные варианты

wolverin ★★★
() автор топика
Ответ на: комментарий от rupert

А по какой причине тебе недоступен C++20?

Для OpenSource пока остаемся в C++17 (местами даже в C++14). Нет смысла переходить на C++20, чтобы оставаться востребованным у большего количества пользователей.

У заказчиков, в лучшем случае, пока только C++17 встречается.

eao197 ★★★★★
()
Ответ на: комментарий от wolverin

пока физически не успел проверить с этими тупыми вайтами

Проверить времени нет, а строчить на LOR есть? Ну тогда в споре кто тупее – wait или wolverin – намечается однозначный победитель.

может из своего опыта кто подскажет и более разумные варианты

А зачем? Вы что, бесплатный OpenSource делаете?

eao197 ★★★★★
()
Последнее исправление: eao197 (всего исправлений: 1)
Ответ на: комментарий от eao197

Представляете бывает так, потому что на телефоне ничего пока не компилируется.

Ну а то что ждать время, засыпать потоки на время в многопоточности когда важны ms мега тупая затея конечно.

wolverin ★★★
() автор топика
Ответ на: комментарий от wolverin

кто подскажет и более разумные варианты

Это такая шутка плохая? Посты про атомики не заметил? Хотя если передавать 0 в ожидающие функции, то скорее всего поток усыпать вообще не будет (т.е. всякие лаги связанные с разрешающей способностью системного таймера и тп не наложатся), во всяком случае это справедливо для гнутой this_thread::sleep_for, но никаких гарантий я не видел.

kvpfs ★★
()
Ответ на: комментарий от wolverin

Ждать по времени имхо ошибка в ДНК

Тебя никто не заставляет ждать секунды. Ты сам можешь выбрать время. Std chrono вплоть до наносекунд может хранить. Для тебя наносекунды это долго?

ox55ff ★★★★★
()
Ответ на: комментарий от ox55ff

Не удастся спать 1 наносекунду, ибо разрешающая способность таймера, планироващика и тп. Минимальный квант времени для сна порядков на 5 выше одной наносекунды.

kvpfs ★★
()
Ответ на: комментарий от ox55ff

Когда один раз ждёшь нормально, а если 100 раз надо так проверить? а если время основного потока бесконечно и везде использовать такой механизм, то даже наносекунды превратятся в часы потраченные на пустые ожидания.

Но если есть механизм проверки без блокировки вообще ни на какое время, лучше же использовать всегда его, даже если это делается условно два раза.

И я постараюсь проверить ВСЕ способы описанные в постах, ибо ценю потраченное время каждого!

wolverin ★★★
() автор топика
Ответ на: комментарий от wolverin

Но если есть механизм проверки без блокировки вообще ни на какое время, лучше же использовать всегда его

Чем этот поток ещё будет заниматься кроме проверкой статуса другого потока? Ты же не будешь гонять пустой цикл? Чем он таким занимется, что на миллисекунду нельзя остановить.

ox55ff ★★★★★
()
Ответ на: комментарий от ox55ff

Конкретно этот без цикла, но если вариант рабочий, то буду использовать в циклах, которых 5+ параллельных будет - один максимально быстрый механизм для всего!

Целиком задача типа видеорегистратор с управлением переферией, по событию пережимающий mjpeg в h264 с отдачей rtsp и все это на слабеньком SoC (через семафоры без всяких вайтов удалось повысить прилично производительность в разных потоках декодирование и кодирования с псевдо очередью из 2х адресов буфера)

Поэтому да, миллисекунды важны, задержки видны на видео, возможно скажутся и на реакции оборудования - следовательно мне надо отказаться от всех ожиданий по времени, копирования памяти и всего что может добавлять «пустые» миллисекунды.

wolverin ★★★
() автор топика