Was das Problem war, für den Fall, dass andere ein ähnliches Problem haben: Nach einigen Diskussionen mit dem Mathworks-Support stellte sich heraus, dass es sich um einen Konflikt zwischen den System-Boost- und den Matlab-Boost-Bibliotheken handelte: Wenn ich mit den System-Boost-Headern kompilierte und mit den (älteren) Matlab-Boost-Bibliotheken verknüpfte, kam es zu einem Segfault. Wenn ich mit System-Boost kompiliert und dynamisch gelinkt habe, aber dann die Matlab-Boost-Bibliotheken dynamisch geladen habe, blieb es für immer hängen.
Statisches Linken zu System-Boost funktioniert, ebenso wie das Herunterladen der richtigen Header für die Version von Boost, mit der Matlab ausgeliefert wird, und das Kompilieren mit diesen. Natürlich haben die Mac-Builds von Matlab keine Versionsnummern in ihren Dateinamen, aber die Linux- und vermutlich auch die Windows-Builds schon. R2011b verwendet boost 1.44, als Referenz.
Ich habe einige multithreaded Code, der gut funktioniert, wenn es direkt kompiliert wird, aber segfaults und/oder Deadlocks, wenn es von einem Matlab aufgerufen wird mex
Schnittstelle. Ich weiß nicht, ob die unterschiedliche Umgebung einen Fehler in meinem Code offenbart, oder was, aber ich kann es nicht herausfinden....
Ich lasse dies auf drei Rechnerkonfigurationen laufen (obwohl es mehrere CentOS-Kisten gibt):
- OSX 10.7, g++ 4.2, boost 1.48, Matlab R2011a (clang++ 2.1 funktioniert auch für Standalone, habe nicht versucht, mex dazu zu bringen, clang zu verwenden)
- altes CentOS, g++ 4.1.2, boost 1.33.1 (debug und nicht debug), Matlab R2010b
- altes CentOS, g++ 4.1.2, boost 1.40 (keine Debug-Versionen installiert), Matlab R2010b
Hier ist eine abgespeckte Version mit diesem Verhalten.
#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");
}
Beachten Sie, dass ich unter boost 1.48 das gleiche Verhalten erhalte, wenn ich den Funktor in eine Standardfunktion umwandle und einfach boost::ref
s zu den Mutexen/Daten als zusätzliche Argumente für boost::thread
. Boost 1.33.1 unterstützt dies jedoch nicht.
Wenn ich es direkt kompiliere, läuft es immer gut - ich habe noch nie erlebt, dass es in irgendeiner Situation fehlschlägt:
$ 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
Bei der Ausführung in Matlab habe ich viele verschiedene Verhaltensweisen gesehen, nachdem ich verschiedene Änderungen am Code usw. vorgenommen habe, allerdings keine Änderungen, die für mich wirklich Sinn ergeben. Aber hier ist, was ich mit dem genauen Code oben gesehen habe:
- Unter OSX / boost 1.48:
- Wenn er mit einer Release-Variante von boost verknüpft ist, erhalte ich einen Segfault, wenn ich versuche, auf eine Near-0-Adresse innerhalb von
boost::thread::start_thread
, die vont1
Konstrukteur. - Wenn er mit einer Debug-Variante von boost verknüpft ist, bleibt er in der ersten Phase ewig hängen.
boost::thread::join
. Ich bin mir nicht ganz sicher, aber ich denke, dass die Worker-Threads zu diesem Zeitpunkt tatsächlich abgeschlossen sind (ich sehe nichts ininfo threads
das sind sie offensichtlich).
- Wenn er mit einer Release-Variante von boost verknüpft ist, erhalte ich einen Segfault, wenn ich versuche, auf eine Near-0-Adresse innerhalb von
- Unter CentOS / boost 1.33.1 und 1.40:
- Mit Release Boost erhalte ich einen Segfault in
pthread_mutex_lock
, die vomboost::thread::join
ent1
. - Mit dem Debugging-Boost hängt es ewig in
__lll_lock_wait
innerhalbpthread_mutex_lock
an der gleichen Stelle. Wie unten gezeigt, sind die Arbeits-Threads zu diesem Zeitpunkt abgeschlossen.
- Mit Release Boost erhalte ich einen Segfault in
Ich weiß nicht, wie man etwas mehr mit den segfaults zu tun, da sie nie auftreten, wenn ich Debugging-Symbole, die mir tatsächlich sagen können, was der Null-Zeiger ist.
Im Fall von "hanging-forever" scheine ich immer so etwas zu bekommen, wenn ich in GDB durchlaufe:
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();
Das sieht so aus, als ob beide Threads abgeschlossen sind, bevor der Aufruf von t1.join()
. Also habe ich versucht, eine sleep(1)
Aufruf im Abschnitt "doing work" zwischen den Sperren; wenn ich durchlaufe, beenden die Threads nach dem Aufruf von t1.join()
und es hängt immer noch ewig fest:
106 t1.join();
(gdb)
[Thread 0x47814940 (LWP 20255) exited]
[Thread 0x48215940 (LWP 20256) exited]
# still hanging
Wenn ich up
hinaus zum doWork
Funktion, results
wird mit denselben Ergebnissen gefüllt, die die Standalone-Version auf diesem Rechner druckt, es sieht also so aus, als würde alles funktionieren.
Ich habe keine Ahnung, was entweder die segfaults oder die verrückte hanging-ness verursacht, oder warum es ist, dass es immer außerhalb von Matlab und nie innerhalb funktioniert, oder warum es mit/ohne Debugging-Symbole unterschiedlich ist, und ich habe keine Ahnung, wie man in herauszufinden, diese gehen. Hat jemand eine Idee?
Auf Anregung von @alanxz habe ich die Standalone-Version des Codes mit den Tools memcheck, helgrind und DRD von valgrind ausgeführt:
- Unter CentOS, das valgrind 3.5 verwendet, gibt keines der Tools einen nicht unterdrückten Fehler aus.
- Unter OSX mit valgrind 3.7:
- Memcheck meldet keine nicht unterdrückten Fehler.
- Helgrind stürzt bei mir ab, wenn es mit einer beliebigen Binärdatei ausgeführt wird (einschließlich z.B.
valgrind --tool=helgrind ls
) unter OSX und beschwert sich über eine nicht unterstützte Anweisung. - DRD gibt über hundert Fehler an.
Die DRD-Fehler sind für mich ziemlich undurchschaubar, und obwohl ich das Handbuch und so weiter gelesen habe, kann ich mir keinen Reim darauf machen. Hier ist der erste, in einer Version des Codes, in der ich den zweiten Worker/Thread auskommentiert habe:
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)
Zeile 66 ist die Konstruktion des Gewindes, und 72 ist die join
Anruf; dazwischen gibt es nichts als Kommentare. Soweit ich das beurteilen kann, heißt das, dass es einen Wettlauf zwischen diesem Teil des Master-Threads und der Initialisierung des Worker-Threads gibt... aber ich verstehe nicht wirklich, wie das möglich ist?
Der Rest der Ausgabe von DRD ist hier Ich habe keinen Nutzen davon.