Запуск потока через конструктор в C++ 11

Я пытаюсь выполнить программу, в которой создаю очередь packaged_task и пытаюсь выполнить packaged_tasks в отдельном потоке. Я получаю следующую ошибку

Ошибка C2664 'std :: function <_Ret (void)> :: function (std :: function <_Ret (void)> &&)': невозможно преобразовать аргумент 1 из 'std :: _ Binder' в '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;
}

не используйте привязку, вместо этого используйте лямбда.

Matthieu Brucher 16.11.2018 16:57
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
0
1
91
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

В вашем коде есть две ошибки. Во-первых, вы неправильно используете свои папки. При привязке функции-члена первый аргумент должен иметь указатель на класс, в вашем случае this. Вот один пример фиксированного кода:

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

Однако использование лямбда-выражений обычно гораздо более интуитивно понятно и синтаксически дружественно, чем связыватели. Вот та же идея, выраженная с помощью лямбды:

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));

С этим кодом могут быть другие проблемы, я исправил только ошибки компиляции.

Ответ принят как подходящий

Вы связываете нестатическую функцию-член класса, поэтому вам нужно указать объект, для которого вызывается функция-член. this отсутствует во всех звонках bind:

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

сделайте то же самое для theMul и theAdd.

Вторая проблема, объект packaged_task не копируется, если вы хотите передать этот объект как параметр push_back вы должны кастовать объект packaged_task в ссылку Rvalue, то 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
}

Сделайте то же самое с методами theAdd и theMul.

2) Ваша функция потока выполняет только одну задачу из очереди. Думаю, там какой-то шлейф отсутствует. Вы хотите выполнить все поступающие задачи в очередь (подробности в комментариях):

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 тоже) не возвращает никакого значения. Что, если я хочу обменять возвращаемое значение из обработчика потока на функцию theAdd?

D_wanna_B_coder 19.11.2018 15:16

@D_wanna_B_coder Если я правильно понимаю: вы хотите создать задачу, поместить задачу в очередь и подождать в theAdd, пока задача не будет выполнена в потоке Handler, затем прочитать значение из будущего и вернуться из Add? Для этого вам нужно вызвать std::future<int> fu = t.get_future(); перед {LOCK; comm_ch_task_queue.push_back(move(t)); NOTIFY_ONE}: так что вы получите future, packaged_task перемещается в очередь, затем вы ждете, пока общее состояние будет готово в будущем. В Handler вам нужно только прочитать packaged_task, вызвать () для задачи и удалить задачу из очереди. Тогда в Handler нельзя использовать future.

rafix07 19.11.2018 16:40

Это именно то, что я хотел, и теперь код работает.

D_wanna_B_coder 19.11.2018 17:23

Другие вопросы по теме