В чем проблема, если у людей была похожая проблема: после некоторых обсуждений с поддержкой Mathworks, это оказалось конфликтом между системным бустом и поставляемыми библиотеками бустеров Matlab: когда я компилировал с заголовки системного повышения и связанные с (более старыми) библиотеками повышения Matlab, это segfaults. Когда я компилировал и динамически связывал с системным бустом, но затем он динамически загружал библиотеки бустеров Matlab, он зависал навсегда.
Статическая связь с системным бустом работает, как и загрузка правильных заголовков для версии буста, с которой поставляется Matlab, и компиляция с ними. Конечно, сборки Mac для Matlab не имеют номеров версий в именах файлов, хотя сборки Linux и, предположительно, Windows. R2011b использует повышение 1.44, для справки.
<ч />
У меня есть некоторый многопоточный код, который отлично работает, когда он компилируется напрямую, но при этом он вызывает ошибки и / или взаимоблокировки, когда он вызывается из интерфейса Matlab mex
. Я не знаю, выявляет ли какая-то другая ошибка в моем коде или что-то в этом роде, но я не могу понять ...
Я запускаю это на трех конфигурациях машины (хотя есть несколько коробок CentOS):
- OSX 10.7, g ++ 4.2, boost 1.48, Matlab R2011a (clang ++ 2.1 также работает в автономном режиме, не пытался заставить mex использовать clang)
- Ancient CentOS, g ++ 4.1.2, boost 1.33.1 (отладка и не отладка), Matlab R2010b
- Ancient 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");
}
Обратите внимание, что на бусте 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:
- Если это связано с повышением варианта выпуска, я получаю segfault, пытающийся получить доступ к адресу, близкому к 0 внутри
boost::thread::start_thread
, вызываемому из конструктора t1
.
- Если он связан с бустом варианта отладки, он навсегда зависает в первом
boost::thread::join
. Я не совсем уверен, но я думаю, что рабочие потоки фактически завершились к этому моменту (не вижу ничего в info threads
, что, очевидно, в них).
- На CentOS / boost 1.33.1 и 1.40:
- С форсированным выпуском я получаю segfault в
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()
. Поэтому я попытался добавить вызов 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:
- В CentOS, использующей valgrind 3.5, ни один из инструментов не дает никаких-подавленные ошибки.
- В OSX с использованием valgrind 3.7:
- Memcheck не выдает неподавленных ошибок.
- Сбой Helgrind при запуске в любом двоичном файле (включаянапример,
valgrind --tool=helgrind ls
) в OSX с жалобой на неподдерживаемую инструкцию. - 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
;между ними нет ничего, кроме комментариев.Насколько я могу судить, это говорит о том, что между этой частью главного потока и инициализацией рабочего потока есть гонка ... но я не совсем понимаю, как это возможно?
Остальныевыход из DRD здесь ;Я ничего не получаю от этого.