无法将“std :: function <void *()>”转换为“void *(*)(void *)”

3
我刚开始使用<functional>库,所以请您耐心等待。以下代码无法编译,并显示以下错误信息:

error: cannot convert std::function<void*()> to void* (*)(void*) for argument 3 to int pthread_create(pthread_t*, const pthread_attr_t*, void* (*)(void*), void*).

我不一定需要在我的代码中使用<functional>,但是在放弃并使用标准函数指针之前,我想看看是否有解决方案。
以下是我的代码:
vector< function< void*() > > signals;
vector< pthread_t > signal_threads;

// Master Signal Handler
void MasterSignalHandler()
{
    for (auto & signal_handler : signals)
    {
        // Create a thread handle
        pthread_t thread_handle;

        // Spawn a thread for the signal handler
        if (0 == pthread_create(&thread_handle, NULL, signal_handler, NULL))
        {
                // Push back the thread handle to the signal thread vector
                signal_threads.push_back(thread_handle);
        }
    }
}

2
你确定不想使用std::thread吗? - Fred Larson
1
错误是准确的,std::function 不是 pthread_create 支持的参数。但是它可以被 std::thread 接受,并且在 POSIX 环境之外也可以工作。 - Drew Dormann
谢谢大家!std::thread 就是我需要的。 - greg
1个回答

7
我可以给你两种回答:一种是展示在C++中如何正确地实现你想做的事情(启动一个线程),另一种是展示如何将std::function对象压缩进pthread_create。前者更加实用,而后者则能够解释pthread_create如何处理上下文。两种方法都有价值,因此:

1. 如何在新线程中调用std::function<void*()>对象

自从C++11以来,标准库就包含了多线程功能。在你的情况下,我们有一个带返回值的函数,所以任务就是:

  1. 异步调用该函数,
  2. 在需要时获取返回值。

最简单的方法是使用std::asyncstd::future<void*>。考虑以下简单示例来进行一个异步调用:

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()从future中获取结果,而是最终使用.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及其在标准库中关联的类为您解决了所有这些问题,因此没有必要重新发明这个特定的轮子。


1
你的实现方式完全符合我的需求,而且非常优雅。我也很感激你提供了详细的解释,这样我就不仅仅是复制粘贴了,还能够学到东西。 - greg

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接