Я полагаю, что есть два вида ответов, которые я могу дать на этот вопрос: я могу показать вам правильный способ сделать то, что вы пытаетесь сделать (начать поток) в C ++, или я могу показать вам, как сжать std::function
объект в pthread_create
. Первый имеет более практическое значение, а второй служит для объяснения того, как pthread_create
обрабатывает контекст. В обоих есть значение, поэтому:
1. Как вызвать std::function<void*()>
объект в новом потоке
Начиная с C ++ 11, стандартная библиотека содержит средства для многопоточности. В вашем случае у нас есть функция с возвращаемым значением, поэтому задача состоит в том, чтобы
- Вызовите функцию асинхронно, а
- Получите возвращаемое значение, когда оно нам понадобится.
Самый простой способ сделать это - использовать std::async
и std::future<void*>
. Рассмотрим этот простой пример для асинхронного вызова one :
std::function<void*()> f; // the function to call
// Call f asynchronously. It is possible to omit the std::launch::async
// parameter, in which case it is up to the implementation when or even
// if a thread is spawned to make the call.
// The future object represents a handle to the void* value that f will
// eventually return (i.e., the future value).
std::future<void*> fut = std::async(std::launch::async, f);
// Do some other stuff while we're waiting
// get f's return value. This will wait if the thread is not yet finished.
void *value_returned_by_f = fut.get();
В вашем случае вы хотите вызывать более одной функции и получать возвращаемые значения на досуге, поэтому вы хотите где-то хранить объекты std::future<void*>
. Как повезет, структура вашего кода может быть в основном сохранена:
vector<function<void*()>> signals;
vector<std::future<void*>> signal_futures;
// Master Signal Handler
void MasterSignalHandler()
{
for (auto & signal_handler : signals)
{
signal_futures.push_back(std::async(std::launch::async, signal_handler));
}
}
Позже вы можете получить возвращаемые значения из signal_futures
на досуге.
Обратите внимание, что если функции не возвращают значения, вы можете использовать std::thread
вместо std::future
:
vector<function<void()>> signals;
vector<std::thread> signal_threads;
// Master Signal Handler
void MasterSignalHandler()
{
for (auto & signal_handler : signals)
{
signal_threads.emplace_back(signal_handler);
}
}
Вместо .get()
результата из будущего, вы в конечном итоге .join()
потоков. Использование std::future<void>
также сработает, так что на самом деле это дело вкуса.
2. Как заставить std::function<void*()>
в pthread_create
Мы только что обсудили разумный способ решения проблемы, а теперь давайте посмотрим на грязные биты. Причина, по которой исходный код не компилируется, состоит в том, что pthread_create
ожидает адрес памяти, указывающий на функцию (т. Е. Машинный код), которую она может выполнить, и что signal_handler
не такой адрес памяти, а сложный объект с состоянием. pthread_create
не может справиться с этим.
Однако: если вы посмотрите внимательно, вы заметите, что функция pthread_create
ожидает, принимает аргумент void*
, и что pthread_create
принимает аргумент void*
позже в своей подписи - этот последний аргумент передается функция, когда поток запущен. Если бы мы действительно хотели, мы могли бы использовать этот механизм, и на самом деле в системах на основе POSIX std::thread
, как правило, сделает именно это для вас. Например:
void *thread_stub(void *context) {
// context is static_cast<void*>(&f) below. We reverse the cast to
// void* and call the std::function object.
std::function<void*()> &func = *static_cast<std::function<void*()>*>(context);
return func();
}
// ...
std::function<void*()> f;
// IMPORTANT: Used this way, f must not go out of scope while the thread
// is running because the thread holds a pointer to it!
pthread_t thread_handle;
pthread_create(&thread_handle, NULL, thread_stub, &f);
Обратите внимание, что я не рекомендую делать это таким образом. Это ужасно, и у него есть проблема, упомянутая в комментарии выше. std::thread
и связанные с ним классы в стандартной библиотеке решают все эти проблемы за вас, поэтому нет необходимости заново изобретать это конкретное колесо.