将lambda推送到C ++ STL队列会导致分段错误

时间:2017-08-31 08:11:57

标签: c++ lambda stl android-ndk

我想将lambda推入队列中。这个lambda将被poped并执行到另一个线程中。当前线程将等待lambda执行并返回一些结果:

void * EglThread::execute(const std::function<void * ()> f) {
    std::condition_variable lambdaExecutedCond;
    void * out;
    bool exceptionFlag = false;
    std::exception exception;

    std::unique_lock<std::mutex> lk(this->mutex);
    this->queue.push([&]() -> void {
        std::unique_lock<std::mutex> __unused lock(this->mutex);
        lambdaExecutedCond.notify_one();

        try {
            out = f();
        } catch (const std::exception & e) {
            exceptionFlag = true;
            exception = e;
        }
    });
    this->cond.notify_all();
    lambdaExecutedCond.wait(lk);

    if (exceptionFlag) {
        throw exception;
    } else {
        return out;
    }
}

void EglThread::run() {
    while (true) {
        std::unique_lock<std::mutex> lk(this->mutex);
        if (this->queue.empty()) {
            if (this->flagShutdown) {
                break;
            } else {
                this->cond.wait(lk);
            }
        } else {
            this->queue.front()();
            this->queue.pop();
        }
    }
}

push操作期间出现Segmentation fault错误。日志看起来像:

EglThread::execute().

Queue pushing...

Segmentation fault

我无法弄清楚可能出现的问题。 queue对象声明为类成员,没有对其构造函数的激活调用。就像这样:

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

1 个答案:

答案 0 :(得分:1)

查看示例(简化):

Log<ANDROID_LOG_INFO>("Queue pushing...\n");
this->queue.push([&]() -> void { /*...*/ } );
Log<ANDROID_LOG_INFO>("Queue pushed\n");

我们可以看到问题出在queue.push部分。 这是你应该关注的内容。

可能的原因是:

  1. queue未正确初始化,
  2. 在创建lambda“对象”期间,可能会复制一些导致此分段错误的数据。
  3. 对于第一个原因,我做了一个重复代码的最小例子: 差异在于我将队列放入函数中。

    void execute( const std::function<void* ()> f )
    {
        std::queue<std::function<void* ()>> queue;
    
        queue.push( [ & ]() -> void*
        {
            return f();
        } );
    }
    
    int main()
    {
        execute( []() -> void*
        {
            return nullptr;
        } );
    }
    

    这没问题。但这是单线程的!需要更多信息来解释这些函数的执行方式/时间。特别是竞争条件。

    仅仅因为你使用互斥锁,并不意味着你是安全的。

    第二个原因更难以追查。

    对于调试,在lambda中,我会一次注释掉每一行,看看这是否有助于克服分段错误。 然后我们可以更容易地找到原因。