weak_ptr, shared_ptr в лямбда-списке захвата и std :: queue :: устанавливать - PullRequest
2 голосов
/ 18 февраля 2020

У меня есть функция, которая ставит лямбда в очередь, используя std::queue's встроенную функцию emplace. Я создал shared_ptr объект (задачу), который позже я бы запечатлел в лямбде.

    template<typename Func, typename... Args>
    auto submit(Func&& f, Args&&... args)
    {
        using result_type = std::result_of_t<Func(Args...)>;
        using pckg_task_type = std::packaged_task<result_type()>;

        auto task = std::make_shared< pckg_task_type >(
                    std::bind(std::forward<Func>(f), std::forward<Args>(args)...) );
        ...
    }

Это та часть, которая создает путаницу:

Случай 1: Прекрасно работает

            tasks.emplace(
                [task](){
                    auto wptr = std::weak_ptr<pckg_task_type>(task);
                    if( auto p = wptr.lock() )
                    {
                        (*p)();
                    }
                    else
                        throw std::runtime_error("weak error");
                }
            );

Случай 2: Который сразу вызывает исключение

                tasks.emplace(
                [wc = std::weak_ptr<pckg_task_type>(task)](){
                    if( auto p = wc.lock() )
                    {
                        (*p)();
                    }
                    else
                        throw std::runtime_error("weak error");
                }
            );

С задачами, определенными как

std::queue< std::function<void()> > tasks;

Вызов Лямбда из случая 2 без включения ее в очередь не вызывает исключения.

Может кто-нибудь объяснить разницу между случаями выше? В чем проблема?

Ответы [ 2 ]

3 голосов
/ 18 февраля 2020

Проблема в том, что в случае 2 общий указатель task нигде не сохраняется, а слабый указатель wc, сохраненный в лямбда-захвате, истекает, как только вы вернетесь из submit. Когда вызывается лямбда, wc.lock() возвращает нулевой указатель.

Вам, вероятно, нужно сохранить task shared_ptr где-нибудь на время жизни объекта функции в очереди. Или вам вообще не нужен weak_ptr, и вы можете просто сохранить task shared_ptr в лямбда-захвате и использовать его непосредственно в теле лямбды.

1 голос
/ 18 февраля 2020

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

Во втором случае захватывается только слабый указатель, общий указатель выходит из области действия в конце отправки, и, по-видимому, других ссылок на него нет, поэтому указатель удаляется. Это будет работать только в том случае, если ваша лямбда начинает выполнение и блокирует указатель до окончания отправки.

...