从matlab使用boost :: threads时挂起和/或段错误,而不是直接调用时

时间:2012-01-25 23:08:50

标签: c++ multithreading matlab boost mex

问题是什么,如果人们遇到类似的问题:在与Mathworks支持进行一些讨论之后,结果发现系统提升与Matlab的提升库之间存在冲突:当我使用系统升级头编译并与(较旧的)Matlab boost库链接时,它会分段。当我编译并动态链接系统boost但它动态加载Matlab boost库时,它永远挂起。

静态链接到系统提升工作,下载Matlab附带的boost版本的正确标头,并与之进行编译。当然,Matlab的Mac版本在文件名中没有版本号,尽管Linux和Windows构建版本都有。 R2011b使用boost 1.44作为参考。


我有一些多线程代码在直接编译时工作正常,但在从Matlab mex接口调用时会出现段错误和/或死锁。我不知道不同的环境是否暴露了我的代码中的缺陷,或者是什么,但我无法弄明白......

我在三台机器配置上运行它(虽然有几个CentOS盒子):

  • OSX 10.7,g ++ 4.2,提升1.48,Matlab R2011a(clang ++ 2.1也适用于独立,避让地试图让mex使用clang)
  • 古代CentOS,g ++ 4.1.2,提升1.33.1(调试而非调试),Matlab R2010b
  • 古代CentOS,g ++ 4.1.2,boost 1.40(未安装调试版),Matlab R2010b

这是一个具有此行为的简化版本。

#include <queue>
#include <vector>

#include <boost/thread.hpp>
#include <boost/utility.hpp>

#ifndef NO_MEX
#include "mex.h"
#endif

class Worker : boost::noncopyable {
    boost::mutex &jobs_mutex;
    std::queue<size_t> &jobs;

    boost::mutex &results_mutex;
    std::vector<double> &results;

    public:

    Worker(boost::mutex &jobs_mutex, std::queue<size_t> &jobs,
           boost::mutex &results_mutex, std::vector<double> &results)
        :
            jobs_mutex(jobs_mutex), jobs(jobs),
            results_mutex(results_mutex), results(results)
    {}

    void operator()() {
        size_t i;
        float r;

        while (true) {
            // get a job
            {
                boost::mutex::scoped_lock lk(jobs_mutex);
                if (jobs.size() == 0)
                    return;

                i = jobs.front();
                jobs.pop();
            }

            // do some "work"
            r = rand() / 315.612;

            // write the results
            {
                boost::mutex::scoped_lock lk(results_mutex);
                results[i] = r;
            }
        }
    }
};

std::vector<double> doWork(size_t n) {
    std::vector<double> results;
    results.resize(n);

    boost::mutex jobs_mutex, results_mutex;

    std::queue<size_t> jobs;
    for (size_t i = 0; i < n; i++)
        jobs.push(i);

    Worker w1(jobs_mutex, jobs, results_mutex, results);
    boost::thread t1(boost::ref(w1));

    Worker w2(jobs_mutex, jobs, results_mutex, results);
    boost::thread t2(boost::ref(w2));

    t1.join();
    t2.join();

    return results;
}

#ifdef NO_MEX
int main() {
#else
void mexFunction(int nlhs, mxArray **plhs, int nrhs, const mxArray **prhs) {
#endif
    std::vector<double> results = doWork(10);
    for (size_t i = 0; i < results.size(); i++)
        printf("%g ", results[i]);
    printf("\n");
}

请注意,在boost 1.48上,如果我将仿函数更改为标准函数并将boost::ref作为boost::thread的额外参数传递给互斥锁/数据,我会得到相同的行为。但是,Boost 1.33.1并不支持这一点。


当我直接编译它时,它总是运行良好 - 我在任何情况下都没有看到它失败:

$ g++ -o testing testing.cpp -lboost_thread-mt -DNO_MEX
$ ./testing
53.2521 895008 5.14128e+06 3.12074e+06 3.62505e+06 1.48984e+06 320100 4.61912e+06 4.62206e+06 6.35983e+06

从Matlab运行后,我对代码进行了不同的调整后看到了很多不同的行为,尽管没有任何实际上对我有任何改变。但这是我在上面的确切代码中看到的内容:

  • 在OSX / boost 1.48上:
    • 如果它与版本变体提升相关联,我会尝试访问boost::thread::start_thread内的近0地址,从t1&#39}调用构造
    • 如果它与调试变量提升相关联,它会在第一个boost::thread::join中永久挂起。我并不完全确定,但我认为工作线程此时已经完成(在info threads中看不到任何显然属于他们的东西)。
  • 在CentOS / boost 1.33.1和1.40上:
    • 通过发布提升,我在pthread_mutex_lock中获得了一个段错误,通过boost::thread::join上的t1进行了调用。
    • 通过调试提升,它会在同一位置__lll_lock_wait内的pthread_mutex_lock内永久挂起。如下所示,此时工作线程已完成。

我不知道如何使用segfaults做更多的事情,因为当我有调试符号可以实际告诉我空指针是什么时,它们永远不会发生。

在永远悬空的情况下,如果我在GDB中踩到,我似乎总能得到这样的东西:

99      Worker w1(jobs_mutex, jobs, results_mutex, results);
(gdb) 
100     boost::thread t1(boost::ref(w1));
(gdb) 
[New Thread 0x47814940 (LWP 19390)]
102     Worker w2(jobs_mutex, jobs, results_mutex, results);
(gdb) 
103     boost::thread t2(boost::ref(w2));
(gdb) 
[Thread 0x47814940 (LWP 19390) exited]
[New Thread 0x48215940 (LWP 19391)]
[Thread 0x48215940 (LWP 19391) exited]
105     t1.join();

在调用t1.join()之前,确实看起来两个线程都已完成。所以我尝试在&#34;做工作&#34;中添加sleep(1)电话。锁之间的部分;当我踩过时,线程在调用t1.join()后退出,它仍然永远挂起:

106     t1.join();
(gdb)
[Thread 0x47814940 (LWP 20255) exited]
[Thread 0x48215940 (LWP 20256) exited]
# still hanging

如果我up doWork函数,results填充了独立版本在此计算机上打印的相同结果,那么它看起来就像所有正在经历的一样。

我不知道导致段错误或疯狂挂起的原因是什么,或者为什么它总是在Matlab之外工作而从不在里面,或者为什么它与调试/不调试有什么不同符号,我不知道如何着手解决这个问题。有什么想法吗?


在@ alanxz的建议下,我在valgrind的memcheck,helgrind和DRD工具下运行了独立版本的代码:

  • 在使用valgrind 3.5的CentOS上,没有任何工具可以提供任何非抑制错误。
  • 使用valgrind 3.7在OSX上:
    • Memcheck没有给出任何非抑制错误。
    • 在OSX上运行任何二进制文件(包括例如valgrind --tool=helgrind ls)时,Helgrind为我崩溃,抱怨指令不受支持。
    • DRD提供了一百多个错误。

DRD错误对我来说非常难以理解,虽然我已阅读过手册等等,但我无法理解它们。这是第一个,在我注释掉第二个工作者/线程的代码版本中:

Thread 2:
Conflicting load by thread 2 at 0x0004b518 size 8
   at 0x3B837: void boost::call_once<void (*)()>(boost::once_flag&, void (*)()) (in /usr/local/boost/boost_1_48_0/stage/lib/libboost_thread-mt-d.dylib)
   by 0x2BCD4: boost::detail::set_current_thread_data(boost::detail::thread_data_base*) (in /usr/local/boost/boost_1_48_0/stage/lib/libboost_thread-mt-d.dylib)
   by 0x2BA62: thread_proxy (in /usr/local/boost/boost_1_48_0/stage/lib/libboost_thread-mt-d.dylib)
   by 0x2D88BE: _pthread_start (in /usr/lib/system/libsystem_c.dylib)
   by 0x2DBB74: thread_start (in /usr/lib/system/libsystem_c.dylib)
Allocation context: Data section of r/local/boost/boost_1_48_0/stage/lib/libboost_thread-mt-d.dylib
Other segment start (thread 1)
   at 0x41B4DE: __bsdthread_create (in /usr/lib/system/libsystem_kernel.dylib)
   by 0x2B959: boost::thread::start_thread() (in /usr/local/boost/boost_1_48_0/stage/lib/libboost_thread-mt-d.dylib)
   by 0x100001B54: boost::thread::thread<boost::reference_wrapper<Worker> >(boost::reference_wrapper<Worker>, boost::disable_if<boost::is_convertible<boost::reference_wrapper<Worker>&, boost::detail::thread_move_t<boost::reference_wrapper<Worker> > >, boost::thread::dummy*>::type) (thread.hpp:204)
   by 0x100001434: boost::thread::thread<boost::reference_wrapper<Worker> >(boost::reference_wrapper<Worker>, boost::disable_if<boost::is_convertible<boost::reference_wrapper<Worker>&, boost::detail::thread_move_t<boost::reference_wrapper<Worker> > >, boost::thread::dummy*>::type) (thread.hpp:201)
   by 0x100000B50: doWork(unsigned long) (testing.cpp:66)
   by 0x100000CE1: main (testing.cpp:82)
Other segment end (thread 1)
   at 0x41BBCA: __psynch_cvwait (in /usr/lib/system/libsystem_kernel.dylib)
   by 0x3C0C3: boost::condition_variable::wait(boost::unique_lock<boost::mutex>&) (in /usr/local/boost/boost_1_48_0/stage/lib/libboost_thread-mt-d.dylib)
   by 0x2D28A: boost::thread::join() (in /usr/local/boost/boost_1_48_0/stage/lib/libboost_thread-mt-d.dylib)
   by 0x100000B61: doWork(unsigned long) (testing.cpp:72)
   by 0x100000CE1: main (testing.cpp:82)

第66行是线程的构造,72是join调用;除了之间的评论之外什么也没有。据我所知,这就是说主线程的那一部分与工作线程的初始化之间存在竞争......但是我并不真正理解这是怎么回事#&#39;有可能吗?

DRD is here的其余输出;我没有得到任何东西。

2 个答案:

答案 0 :(得分:1)

你确定这是最简单的段错误和/或挂起吗?如果DRD的结果确实表明线程构造和连接之间存在争用条件,则听起来您的代码可能没有错(特别是因为您实际上并未使用任何mex特定功能,而只是在mex会触发错误。)

也许只试试这个版本:

#include <boost/thread.hpp>

void doNothing() { return; }

void doWork() {
    boost::thread t1(doNothing);
    t1.join();
}

#ifdef NO_MEX
int main() {
#else
#include "mex.h"
void mexFunction(int nlhs, mxArray **plhs, int nrhs, const mxArray **prhs) {
#endif
    doWork();
}

这绝对不应该在mex下直接或挂起或直接编译 - 所以如果它确实存在,那不是你的错误,如果没有,也许你可以逐步缩小版本和版本之间的距离。这个可以找到导致错误的添加。

答案 1 :(得分:0)

您的代码有一个失败点:当任何线程延迟超过2秒时,锁定构造函数中的timed_lock调用可能会超时,互斥锁获得了,无论如何你都可以访问受保护的结构。如果使用定时互斥锁,则必须测试锁实际上是锁定互斥锁还是仅仅超时。可以通过调用锁'owns_lock()方法来检查。

我没有看到这里定时互斥的任何动机,你提到“在取出定时线程之后”,但我仍然怀疑这个互斥超时错误在这里是错误的。当您使用普通timed_mutex替换mutex时,是否仍会出现此错误?