Member 13508014 Ответов: 1

Почему основной поток выходит из строя, когда дочерний поток вызывает функцию обратного вызова основного потока


Я пытаюсь понять некоторые основы многопоточности. Я написал простую программу, как показано ниже. Когда дочерний поток вызывает функцию обратного вызова, в основном потоке наблюдается сбой (здесь область действия функции обратного вызова находится в основном потоке. Насколько я понимаю, это и есть сфера применения. Но я предполагаю, что это что-то связанное с областью действия, которая ведет к краху)

#include <iostream>
#include <thread>

using namespace std;

void t1_exec(void (*notfiy_func)());
void main_observer();

int main()
{
	std::thread t1(t1_exec, main_observer);
	int i = 0;
	while (i < 200)
	{
		i++;
		cout << "In main thread" << endl;
	}

	return (0);
}

void t1_exec(void (*notify_func)())
{
	int i = 0;
	while (i < 5)
	{
		i++;
		cout << "Inside T1" << endl;
	}

	notify_func();
	
}

void main_observer()
{
	cout << "Inside Observer" << endl;

	exit(1);
}


Что я уже пробовал:

Попробовал увеличить время выполнения основного потока за счет увеличения количества циклов (то есть увеличил i до 2000). Но это все равно не помогло остановить крэша.

Leo Chapiro

Что такое void *notfiy_func() ? Где это объявлено? Я пропустил это в вашем коде ...

Richard MacCutchan

Это имя параметра для t1_exec.

Richard MacCutchan

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

1 Ответов

Рейтинг:
1

Jochen Arndt

Родительский поток всегда должен ждать завершения дочерних потоков. Для этого используйте thread::join - ссылка на C++ [^]:

int main()
{
    std::thread t1(t1_exec, main_observer);
    cout << "In main thread" << endl;
    t1.join();
    cout << "Child thread has terminated" << endl;
    return (0);
}

Но наиболее вероятной причиной сбоя вашей программы является то, что вы звоните exit в вашей main_observer() функция:
Вызов этой функции уничтожает все объекты со статической длительностью: программа с несколькими запущенными потоками не должна вызывать exit (см. quick_exit для аналогичной функции, которая не влияет на статические объекты).


Member 13508014

thread::join - это блокирующий вызов? Если это так, я могу не захотеть его использовать. Потому что намерение состоит не в том, чтобы блокировать основной поток и позволить ему делать свою работу.

Jochen Arndt

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

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

Но никогда не вызывайте exit() с многопоточными приложениями. Вы можете вызвать std::terminate (), чтобы завершить поток. Но в вашем случае нет никакой причины, потому что поток завершается при возвращении из рабочей функции.

Member 13508014

Это было бы идеальным решением, если бы оно сработало. Но нет никакого успеха, когда thread::join() вызывается перед возвратом в main(). Это проясняет мой вопрос, который был у меня в голове. Если thread::join() не выполняется, то как это может повлиять? Именно это и происходит, когда join() вызывается перед return, верно?

И когда thread::join() вызывается перед циклом while в main, main thread вообще не получает шанса выполнить цикл, как и ожидалось.

Jochen Arndt

Я никогда не говорил, что использование join позволит избежать аварии. Я упомянул об этом, потому что именно так можно использовать потоки (или использовать отсоединение, что довольно редко).

Главный ответ таков:
"Но наиболее вероятная причина сбоя вашей программы заключается в том, что вы вызываете exit() в своей функции main_observer ()".

Однако Ричард упомянул, что код работает для него. Так что, скорее всего, он также зависит от компилятора и ОС.