Являются ли std:: signal и std:: raise поточно-безопасными?

Стандарты C и С++ поддерживают концепцию сигнала. Тем не менее, стандарт C11 говорит, что функциональный сигнал() нельзя вызывать в многопоточных средах или поведение undefined. Но я думаю, что механизм сигнала по своей природе носит многопоточные среды.

Цитата из стандарта C11 7.14.1.1.7

"Использование этой функции в многопоточной программе приводит к поведению undefined. реализация должна вести себя так, как будто никакая функция библиотеки не вызывает функцию сигнала."

Любые объяснения по этому поводу?

Следующий код является самоочевидным.

#include <thread>
#include <csignal>

using namespace std;

void SignalHandler(int)
{
    // Which thread context here?
}

void f()
{
    //
    // Running in another thread context.
    //
    raise(SIGINT); // Is this call safe?
}

int main()
{
    //
    // Register the signal handler in main thread context.
    //
    signal(SIGINT, SignalHandler);

    thread(f).join();
}

Ответ 1

Но я думаю, что механизм сигнала по своей природе применим для многопоточных сред.

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

Ответ 2

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

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

Ответ 3

Стандартное утверждение C11, что "Использование этой функции в многопоточной программе приводит к поведению undefined", относится конкретно к функции signal(). Поэтому вопрос в том, что использование signal() выполняется "в многопоточной программе".

Термин "многопоточная программа" не определен в стандарте C, насколько я могу судить, но я бы назвал его программой, в которой были созданы и не выполнены несколько потоков выполнения. Это означало бы, что в момент, когда в вашей примерной программе вызывается signal(), программа не является многопоточной, и поэтому поведение программы не соответствует undefined по этому требованию.

(Однако С++ 11 требует, чтобы "все обработчики сигналов имели C-ссылку", [18.10. Другая поддержка времени выполнения [support.runtime] p9]. Так как ваша примерная программа использует обработчик с С++-связью, поведение undefined.)


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

Ответ 4

Я думаю, вы просто неправильно понимаете поведение undefined, которое, к сожалению, сильно перегружено, чтобы означать, что "произойдет плохое". Здесь термин действительно просто означает, что он говорит: стандарт C не делает никаких предположений о том, что означает использовать signal в многопоточном контексте.

В общем случае интерфейс signal/raise в стандарте C сам по себе не очень полезен, а только местозаполнитель для определенных для платформы/ОС вещей, которые определены поверх него.

Таким образом, для взаимодействия между signal и угрозами не предоставляется контракт. Или указано иначе, взаимодействие signal и потоков остается на платформе реализация.