2016-10-12 6 views
2

Я написал небольшое приложение для демонстрации проблемы, это не очень, но он выполняет эту работу.C++ 11: Segfault с std :: thread и лямбда-функцией

#include <functional> 
#include <iostream> 
#include <mutex> 
#include <queue> 
#include <thread> 

class A { 
public: 
    A() : thread_(), tasks_(), mutex_(), a_(99999) {} 

    void Start() { 
    thread_ = std::thread([this]() { Run(); }); 
    } 

private: 
    using Task = std::function<void()>; 

public: 
    void AddTask(const Task& task) { 
    std::lock_guard<std::mutex> lock(mutex_); 

    tasks_.push(task); 
    } 

    bool Empty() { 
    std::lock_guard<std::mutex> lock(mutex_); 

    const bool empty = tasks_.empty(); 

    return empty; 
    } 

    Task GetTask() { 
    std::lock_guard<std::mutex> lock(mutex_); 

    const auto& task = tasks_.front(); 

    tasks_.pop(); 

    return task; 
    } 

    int GetInt() { return a_; } 

    void Join() { thread_.join(); } 

private: 
    void Run() { 
    while (Empty()); 

    (GetTask())(); 
    } 

    std::thread thread_; 
    std::queue<Task> tasks_; 
    std::mutex mutex_; 
    int a_; 
}; 

template <class Base> 
class B : public Base { 
public: 
    using Base::Base; 

    void Start() { 
    Base::Start(); 
std::cout << "A: " << this << std::endl; 
    Base::AddTask([this]() { std::cout << "T: " << this << std::endl; Do(); }); 
    } 

    void Do() { 
    std::cout << "Do: " << this << std::endl; 
    std::cout << "GetInt: " << Base::GetInt() << std::endl; 
    } 
}; 

int main() { 
    B<A> app; 
    app.Start(); 
    app.Join(); 
} 

лязг ++ -std = C++ 11 -lpthread test_a.cpp

A: 0x7ffeb521f4e8 
T: 0x21ee540 
Do: 0x21ee540 
GetInt: 0 

Обратите внимание на изменение в 'это', а значение 0 для 'GetInt'.

Я действительно потерял здесь ... Любая помощь будет принята с благодарностью,

Спасибо.

+0

Я смотрел на это немного назад, рад видеть, что вы заметили, что это не относится к tcmalloc. Когда я посмотрел, я нашел «причину» проблемы. Последовательность: 'app.Start()', основной поток добавляет задачу, основной поток достигает конца области и существ, разрушающих 'приложение'. Часть 'B' разрушается, блоки« A »в потоке. Thread поднимает задачу, и вы получаете неопределенное поведение, когда оно вызывает 'B :: Do', потому что часть' B' больше не существует. Тем не менее, я не опубликовал ответ, потому что я действительно озадачен тем, почему значение 'this' изменяется. :) Я постараюсь понять это завтра, если никто не выяснит это. – GManNickG

+0

(Я имею в виду, я мог бы просто заключить: «это неопределенное поведение, что-то происходит, не делайте этого». Но я хотел бы знать, почему «это» когда-либо изменится в практических реализациях.) – GManNickG

+0

Спасибо GManNickG. Не могли бы вы подтвердить, что я исправил проблему, которую вы определили? Если это так, я должен упомянуть, что выход программы не изменяется. – Nick

ответ

1

я уменьшил свою репродукцию на:

#include <functional> 
#include <iostream> 
#include <queue> 

struct foo { 
    using Task = std::function<void()>; 

    void Test() { 
    std::cout << "In Test, this: " << this << std::endl; 
    AddTask([this] { std::cout << "In task, this: " << this << std::endl; }); 
    } 

    void AddTask(const Task& task) { 
    tasks_.push(task); 
    } 

    Task GetTask() { 
    const auto& task = tasks_.front(); 
    tasks_.pop(); 
    return task; 
    } 

    std::queue<Task> tasks_; 
}; 

int main() { 
    foo f; 
    f.Test(); 
    auto func = f.GetTask(); 
    func(); 
} 

Вы видите проблему сейчас? Проблема заключается с:

const auto& task = tasks_.front(); 
tasks_.pop(); 

Здесь вы захватить ссылку на объект, то вы скажите очереди, чтобы идти вперед и удалить этот объект. Ваша ссылка теперь болтается, и возникает хаос.

Вы должны переместить его вместо:

Task GetTask() { 
    auto task = std::move(tasks_.front()); 
    tasks_.pop(); 
    return task; 
} 
+0

Ahhhh! Большое спасибо :) – Nick

 Смежные вопросы

  • Нет связанных вопросов^_^