当从matlab使用boost::线程时,挂起和/或段错误,而不是直接调用时
hang and/or segfault when using boost::threads from matlab, not when called directly
问题是什么,以防人们有类似的问题:在与Mathworks支持人员进行了一些讨论之后,结果发现这是系统boost和Matlab附带的boost库之间的冲突:当我使用系统boost头并与(旧的)Matlab boost库链接时,它会断开。当我编译并动态链接到系统boost时,但是它动态加载了Matlab boost库,它永远挂起了。
静态链接到系统boost工作,就像下载Matlab附带的boost版本的正确头文件并使用它们进行编译一样。当然,Mac版本的Matlab在文件名中没有版本号,尽管Linux和Windows版本有。R2011b使用boost 1.44,供参考。
我有一些多线程代码,当它直接编译时工作良好,但是当它从Matlabmex
接口调用时,会出现分段错误和/或死锁。我不知道不同的环境是否暴露了我的代码中的缺陷,还是什么,但我不能弄清楚....
我在三种机器配置上运行这个(尽管有几个CentOS盒子):
- OSX 10.7, g++ 4.2, boost 1.48, Matlab R2011a (clang++ 2.1也适用于独立,没有试图让mex使用clang)
- 古老的CentOS, g++ 4.1.2, boost 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
s传递给互斥体/数据作为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,我得到一个段错误试图访问
boost::thread::start_thread
内部的近0地址,从t1
的构造函数调用。 - 如果它被链接到一个debug-variant boost,它永远挂在第一个
boost::thread::join
。我不完全确定,但我认为工作线程实际上已经完成在这一点上(没有看到任何在info threads
,显然是他们)。
- 如果它链接到一个版本变体boost,我得到一个段错误试图访问
- 在CentOS/boost 1.33.1和1.40:
- 与发布boost,我得到
pthread_mutex_lock
段故障,从t1
上的boost::thread::join
调用。 - 与调试boost,它永远挂在
__lll_lock_wait
内pthread_mutex_lock
在同一个地方。如下所示,工作线程已经完成。
- 与发布boost,我得到
我不知道如何处理段错误,因为当我有调试符号可以告诉我空指针是什么时,它们永远不会发生。
在永远挂起的情况下,如果我在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()
之前都完成了。所以我试着在锁之间的"做工作"部分添加一个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上,没有一个工具给出任何非抑制错误。
- 在OSX使用valgrind 3.7:
- Memcheck不给出任何非抑制错误。 当我在OSX上运行任何二进制文件(包括例如
valgrind --tool=helgrind ls
)时,Helgrind崩溃,抱怨不支持的指令。 - DRD给出超过100个错误。
DRD错误对我来说是相当难以理解的,尽管我已经阅读了手册等,但我无法理解它们。下面是第一个,关于我注释掉第二个worker/线程的代码版本:
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
调用;中间除了评论什么都没有。据我所知,这是说在主线程的那一部分和工作线程的初始化之间存在竞争……但我真的不明白这怎么可能?
DRD的其余输出在这里;我什么也得不到。
你确定这是最简单的segfault和/或挂起的情况吗?如果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
或直接编译下发生分段故障或挂起-所以如果它发生了,这不是你的错误,如果它没有,也许你可以逐渐缩小你的版本和这个版本之间的距离,以找到导致错误的添加。
在您的代码中有一个故障点:当任何线程延迟超过2秒时,锁构造函数中的timed_lock
调用可能超时,互斥锁没有获得,并且您仍然访问受保护的结构。如果使用定时互斥锁,则必须测试锁是否真的锁定了互斥锁,还是仅仅是超时了。这可以通过调用锁的owns_lock()
方法来检查。
我看不出这里使用定时互斥锁的任何动机,你提到"在取出定时线程的东西之后",但我仍然怀疑这个互斥锁超时错误是错误的。当你用普通的mutex
替换timed_mutex
时,这个bug还会发生吗?
- C++ 结构错误"调用'erase'没有匹配函数
- 错误:调用 .. at return 语句时没有匹配函数
- 从C++代码抛出错误调用 JavaScript 回调函数 - nbind
- 错误:调用"make_pair"没有匹配函数
- 错误:调用 std::thread 没有匹配函数
- SFML 中的分段错误 调用 sf::窗口::关闭后
- 错误:调用函数指针没有匹配函数
- 汇编错误:调用基本型构造函数时无效的转换
- 错误:调用计数器::计数器()没有匹配函数
- C++ 通过函数指针错误调用成员函数
- 错误:调用 cargaison::cargaison() 时没有匹配函数
- 简单矩阵类,错误:调用私有构造函数
- 带有返回类型的错误调用模板到会员函数
- 错误:调用没有匹配函数(无法解决?
- 从 C 代码链接错误调用C++函数(使用 gcc 进行链接)
- 错误:调用 'pybind11::buffer_info::buffer_info 没有匹配函数
- 错误:调用terminate引发exceptionAbort陷阱:6
- 错误调用功能无法转换类型
- C++ 中的文件处理错误 - 调用"std::basic_fstream<char, std::char_traits<char> >::open(const char[8],
- 对成员函数set_value的错误调用是不明确的(在 xcode 中使用 pugixml 库)