【问题标题】:Launching thread through Constructor in C++11在 C++11 中通过构造函数启动线程
【发布时间】:2019-04-19 19:47:53
【问题描述】:

我正在尝试执行创建 packaged_task 队列的程序,并尝试在单独的线程中执行 packaged_tasks。我收到以下错误

错误 C2664 'std::function<_ret>::function(std::function<_ret> &&)':无法从 'std::_Binder 转换参数 1 ' 到 'std::nullptr_t'

我的代码如下所示。任何人都可以指导为什么会发生此错误吗?

/*
****************************************************************************************************
                                        The Header File
****************************************************************************************************
*/


class CommandChannel {
private:
    std::thread commChannelThread;
    std::mutex commChannelmu;
    std::condition_variable commChannelcv;


    struct taskRequest
    {};
    struct taskResponse
    {};

    /* Creates a queue of packaged tasks that accepts one iu */
    std::deque< std::packaged_task<int()>> comm_ch_task_queue;

    void threadHandler(void);

protected:
    int p_impl_theAdd(int a, int b);
    int p_impl_theMul(int a, int b);
    int p_impl_theDiv(int a, int b);
public:
    /*Constructor and the Destructor*/
    CommandChannel();
    ~CommandChannel();

    /*The public functions */
    int theAdd(int a, int b);
    int theMul(int a, int b);
    int theDiv(int a, int b);
};

    /*
    ****************************************************************************************************
                                            The Implementation File
    ****************************************************************************************************
    */

/* Implementation Functions */
int CommandChannel::p_impl_theAdd(int a, int b)
{
    return a+b;
}

int CommandChannel::p_impl_theMul(int a, int b)
{
    return a*b;
}

int CommandChannel::p_impl_theDiv(int a, int b)
{
    return a / b;
}


/* COnstructors and Destructors */
CommandChannel::CommandChannel()
{
    /*Creating a new thread that runs the threadHandler function*/
    commChannelThread = std::thread(&CommandChannel::threadHandler, this);
}
CommandChannel::~CommandChannel()
{
    if (commChannelThread.joinable()) {
        commChannelThread.join();
        std::cout << "Command Channel Thread Joined " << std::endl;
    }
    else
        std::cout << "Problem in joining the Command Channel Thread" << std ::endl;
}

/*  User Public Functions  */
int CommandChannel::theAdd(int a, int b)
{
    /* Creating the packaged task with the the implementation pointer */
    std::packaged_task<int()> t(std::bind(&CommandChannel::p_impl_theAdd, a, b));

    /* Pushing the task in the queue */
    {
        std::lock_guard<std::mutex> locker(commChannelmu);
        comm_ch_task_queue.push_back(t);
        commChannelcv.notify_one();
    }

    /* creating the placeholder for the return value */
    std::future<int> fu = t.get_future();

    /* getting the value from the future */
    return fu.get();

}

int CommandChannel::theMul(int a, int b)
{
    /* Create the packaged task with the pimpl */
    std::packaged_task<int()> t(std::bind(&CommandChannel::p_impl_theMul, a, b));

    /* Pushing the task in the queue */
    {
        std::lock_guard<std::mutex> locker(commChannelmu);
        comm_ch_task_queue.push_back(t);
        commChannelcv.notify_one();
    }

    /* Creating the placeholder for the return value */
    std::future<int> fu = t.get_future();

    /*getting the value from the future*/
    return fu.get();
}

int CommandChannel::theDiv(int a, int b)
{
    /* Create the packaged tasks with the pimpl */
    std::packaged_task<int()> t(std::bind(&CommandChannel::p_impl_theDiv, a, b));

    /*Pushing the task in the queue thorigh the mutex locks*/
    {
        std::lock_guard<std::mutex> locker(commChannelmu);
        comm_ch_task_queue.push_back(t);
        commChannelcv.notify_one();
    }
    /* Creating the placeholder for the return value */
    std::future<int> fu = t.get_future();

    /*getting the value from the future*/
    return fu.get();
}


/* 
   Thread Handler

   Pops the elemetns from the queue and then executes them
   the value goes to the called function through future references
*/
void CommandChannel::threadHandler()
{
    std::packaged_task<int()> t;
    {
        std::unique_lock<std::mutex> locker(commChannelmu);
        commChannelcv.wait(locker);
        t = std::move(comm_ch_task_queue.front());
        comm_ch_task_queue.pop_front();
    }
    t();
}


    /*
    ****************************************************************************************************
                                            Main
    ****************************************************************************************************
    */

int main()
{
    CommandChannel api;

    api.theAdd(2, 4);
    api.theDiv(6, 3);
    api.theMul(5, 7);

    return 0;
}

【问题讨论】:

  • 不要使用绑定,而是使用 lambda。

标签: c++ multithreading api sockets c++11


【解决方案1】:

您的代码中有两个错误。首先,您使用的活页夹不正确。绑定成员函数时,第一个参数应该具有指向类的指针,在您的情况下为 this。这是固定代码的一个示例:

std::packaged_task<int()> t(std::bind(&CommandChannel::p_impl_theDiv, this, a, b));

但是,使用 lambdas 通常比活页夹更直观和语法友好。这是用 lambda 表达的相同想法:

std::packaged_task<int()> t([this, a, b]() {return p_impl_theDiv(a, b);});

另外,一旦这个问题在所有地方都得到解决,您将遇到另一个问题 - 您正在复制 packaged_task 对象,同时将它们推入队列。这些对象不可复制,您必须移动。同样,这里是这个固定的一个实例:

comm_ch_task_queue.push_back(std::move(t));

这段代码可能还有其他问题,我只修复了编译错误。

【讨论】:

    【解决方案2】:

    你正在绑定类的非静态成员函数, 所以你需要指定对象 成员函数被调用。 this 不见了 在bind的所有电话中:

    std::packaged_task<int()> t(std::bind(&CommandChannel::p_impl_theDiv, this, a, b));
    

    theMultheAdd 执行相同操作。

    第二个问题,packaged_task对象无法复制, 如果您想将此对象作为参数传递 push_back 你必须投 packaged_task 对象 到右值引用,然后packaged_task 将被移动 成向量。

    comm_ch_task_queue.push_back(std::move(t));
    

    以上都是为了消除编译过程中的错误。

    1) 因为packaged_task 是在theDiv 方法中移动的, 执行移动操作后,您不能为此对象调用任何成员。 推送packaged_task 后,您应该离开theDiv 函数。

    void CommandChannel::theDiv(int a, int b)
    {
        /* Create the packaged tasks with the pimpl */
        std::packaged_task<int()> t(std::bind(&CommandChannel::p_impl_theDiv, this, a, b));
    
        /*Pushing the task in the queue thorigh the mutex locks*/
        {
            std::lock_guard<std::mutex> locker(commChannelmu);
            comm_ch_task_queue.push_back(std::move(t));
            commChannelcv.notify_one();
        }
    
        // access to t is forbidden
    }
    

    theAddtheMul 方法执行相同操作。

    2) 您的线程函数只执行队列中的一项任务。 我认为那里缺少一些循环。您希望将所有传入任务执行到队列中(详细信息在 cmets 中):

    void CommandChannel::threadHandler()
    {
        std::packaged_task<int()> t;
        while (true) // inifite loop to process incoming tasks
        {
            std::unique_lock<std::mutex> locker(commChannelmu);
            commChannelcv.wait(locker,[this](){ return comm_ch_task_queue.size();});
            // wait returns if there is some task to be performed
    
            t = std::move(comm_ch_task_queue.front());
            comm_ch_task_queue.pop_front();
            std::future<int> f = t.get_future();
            t(); // perform task
            std::cout << f.get() << std::endl; // print value
        }
    }
    

    【讨论】:

    • @rafixx07 在这个实现中,theAdd 函数(theMul 和 theDiv 也是)不返回任何值。如果我想将线程 Handler 的返回值交换给 theAdd 函数怎么办?
    • @D_wanna_B_coder 如果我理解正确:您要创建任务,将任务推入队列,然后在theAdd 中等待,直到在Handler 线程中执行任务,然后从未来读取值并从添加?为了做到这一点,你需要在{LOCK; comm_ch_task_queue.push_back(move(t)); NOTIFY_ONE} 之前调用std::future&lt;int&gt; fu = t.get_future();:所以你得到future,packaged_task 被移动到队列中,然后你等待共享状态在未来准备好。在 Handler 中,您只需要读取 packaged_task,在任务上调用 () 并从队列中删除任务。在 Handler 中你不能使用 future。
    • 这正是我想要的,现在代码可以工作了。
    猜你喜欢
    • 1970-01-01
    • 2018-01-23
    • 2012-05-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-06-30
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多