2010-12-07 2 views
1

Ich schreibe eine kleine Hobby-Anwendung in C++ mit Qt. Die Anwendung hat einige Wörterbuch-Dateien beim Start zu lesen, die einige Zeit in Anspruch nimmt, so dass ich erstellte Klasse einen benutzerdefinierten Thread die Wörterbücher im Hintergrund zu analysieren:Seltsame Laufzeitfehler mit einer Multithread-C++ - Qt-Anwendung, die nur im VS-Debugger auftritt

class SetupThread : public QThread 
{ 
    Q_OBJECT 
public: 
    SetupThread(QObject *p_parent); 
    void setDictOutputs(WordDictionary *word, KanjiDictionary *kanji, RadicalDictionary *rad); 
    void run() 
    { 
     emit message("Parsing JMdict dictionary..."); 
     m_wordDict->parseDictionary("dictionaries/JMdict_e.xml"); 

     emit message("Parsing KANJIDIC dictionary..."); 
     m_kanjiDict->parseDictionary("dictionaries/kanjidic2.xml"); 

     emit message("Parsing RADKFILEX dictionary..."); 
     m_radDict->parseDictionary("dictionaries/radkfilex.utf8"); 
    } 

signals: 
    void message(const QString &p_msg); 

private: 
    WordDictionary *m_wordDict; 
    KanjiDictionary *m_kanjiDict; 
    RadicalDictionary *m_radDict; 
}; 

Die drei „Dictionary“ Klassen erstellt werden von mir und sie alle Erben eine gemeinsame Schnittstelle, die Q_OBJECT-Funktionalität enthält, um die Hauptklasse über den Kopf des Setup-Thread im Qt :: QueuedConnection-Modus mit Fortschrittsupdates während der Analyse zu signalisieren, so dass es eine Fortschrittsleiste anzeigen kann. Der Setup-Faden wird von den wichtigsten Klassen Konstruktor wie folgt aufgerufen:

MainForm::MainForm(QWidget *parent, Qt::WFlags flags) 
{ 
    /* ... */ 
    m_wordDict = new WordDictionary(this); 
    m_kanjiDict = new KanjiDictionary(this); 
    m_radDict = new RadicalDictionary(this); 
    m_setupThread = new SetupThread(this); 
    m_setupThread->setDictOutputs(m_wordDict, m_kanjiDict, m_radDict); 
    m_setupThread->start(); 
} 

ich einige Probleme, mit der Anwendung abstürzt beim Beenden gestartet und ich konnte nicht sehen, was das Problem war, so habe ich versucht, es in denen laufen Visual C++ 2008 Debugger. Dann bekomme ich einen großen Absturz bei der Inbetriebnahme:

Unbehandelte Ausnahme bei 0x7568b727 in kanjiflash.exe: Microsoft C++ Ausnahme: std :: exception auf Speicherplatz 0x024ffa1c ..

Die Stack-Trace zeigt :

KernelBase.dll 7568b727()

KernelBase.dll 7568b727()[Rahmen für KernelBase.dll unrichtige und/oder fehlt, keine Symbole unten kann geladen werden]! msvcr90d.dll! _heap_alloc_dbg_impl (unsigned int nSize = 72, int nBlockUse = 1, const char * szFileName = 0x00000000, int nLine = 0, int * errno_tmp = 0x024ff9f8) Zeile 497 + 0xC Bytes C++
msvcr90d.dll! _nh_malloc_dbg_impl (nicht signiert int nGröße = 72, int nhFlag = 0, int nBlockUse = 1, const char * szFileName = 0x00000000, int nLine = 0, int * errno_tmp = 0x024ff9f8) Zeile 239 + 0x19 Byte C++ msvcr90d.dll! _nh_malloc_dbg (unsigned int nSize = 72, int nhFlag = 0, int nBlockUse = 1, const char * szFileName = 0x00000000, int nLinie = 0) Zeile 296 + 0x1d Bytes C++
msvcr90d.dll! malloc (unsigned int nSize = 1) Zeile 56 + 0x15 bytes C++
020bea68()
kanjiflash.exe! SetupThread :: run() Zeile 391 + 0x2c Bytes C++
QtCored4.dll! QThreadPrivate :: start (void * arg = 0x020bd0c8) Linie 317 C++
msvcr90d.dll! _callthreadstartex() Linie 348 + 0xf Bytes C
msvcr90d.dll! _threadstartex (void * PTD = 0x020bd8f0) Zeile 331 C
kernel32.dll! 75.593.677()
ntdll.dll! 77739d72()
ntdll.dll! 77739d45()

Die besondere Linie in SetupThread :: run() bezieht sich dies zu dem ist, wo ich versuche, parseDictionary ("...") auszuführen. Dieser Call-Stack wird von Windows7 64bit bezogen. In Windows XP 32bit hatte ich ein identisches Problem, der einzige Unterschied war, dass der Stack zum Konstruktor eines QString (const char * ch) von SetupThread :: run() ging, wo er sich beschwerte und den * ch-Puffer als ein paar zeigte Zeichen von Müll.

Jetzt ist das seltsame Ding, das nur innerhalb des Debuggers geschieht. Sowohl die Debug- als auch die Release-Konfiguration funktionieren außerhalb des Debuggers.Während ich mit der Anwendung herumfummelte, fand ich den Fehler, der dazu führte, dass ich den Debugger überhaupt benutzte, aber ich frage mich, was wirklich passiert und was ich tun werde, wenn ich den Debugger eines Tages benutzen muss. Da ich mich mit Multithread-Programmierung nicht auskenne, bin ich mir nicht sicher, ob es überhaupt möglich ist, sie in einem Debugger sinnvoll auszuführen, oder ob ich etwas falsch mache, zum Beispiel Zeiger auf die Hauptklasse im Worker-Thread zu bearbeiten (Zugriffsverletzung?). Jeder Einblick sehr geschätzt.

+0

schwer zu sagen, ohne den Code zu sehen, um den Thread zu stoppen/zu beenden, aber angenommen, dass Ihr MainForm-Objekt gelöscht wird, während der Thread noch läuft, sind Sie garantiert in Schwierigkeiten. Es scheint auch, dass die exe funktioniert, wenn kein Debugger angeschlossen ist, während sie sich tatsächlich genau gleich verhält, aber das Problem nicht angezeigt wird, weil bestimmte Debugging-Funktionen nur unter dem Debugger aktiviert sind. – stijn

+0

Der Absturz erwähnt eine std :: exception können Sie Ihren Haupt-und Ihren Thread umgeben: mit einem Versuch catch laufen und sehen, von wo die Ausnahme ausgelöst wird? –

+0

@ David: Ich tat, wie Sie vorgeschlagen, aber meine catch-Anweisungen scheinen nicht ausgeführt werden, auch mit catch (...), und VS beschwert sich über "unbehandelte Ausnahme". Ein anderes Geheimnis. – neuviemeporte

Antwort

1

Ich bin heute beim Durchlaufen des Programms im Debugger auf eine Antwort gestoßen. Basierend auf dieser Stack-Trace Ich habe vor:

kernel32.dll 7d4e2366()
[Frames unten falsch sein können und/oder fehlen, für kernel32.dll geladen Symbole]
kernel32.dll 7d4e2366!()
QtCored4.dll! QString :: QString (const char * ch = 0x0265ff24) Zeile 427 + 0x12 Bytes C++
KanjiFlash.exe! SetupThread :: run() Linie 400 + 0x2c Bytes C++
QtCored4.dll! QThreadPrivate :: start (void * arg = 0x02137f68) Zeile 317 C++
msvcr90d.dll! _callthreadstartex() Zeile 348 + 0xf Byte C
msvcr90d.dll! _threadstartex (void * PTD = 0x02138828) Zeile 331 C
kernel32.dll! 7d4dfe21()

... und die "const char * ch" Puffer enthalten nach dem Absturz Müll zu sehen Ich hatte angenommen, dass das String-Literal irgendwie korrumpiert wurde, und das war der Grund für die Ausnahme. Aber ich habe versucht, in die erste parseDictionary() -Funktion zu treten, und es hat funktioniert. Die beggining der Funktion sieht wie folgt aus:

void WordDictionary::parseDictionary(const QString &p_dictPath) 
{ 
    // open XML file 
    QFile file(p_dictPath); 
    if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) 
    { 
     throw std::exception("Unable to open dictionary file in WordDictionary constructor"); 
    } 
    /* ... */ 

ich den Absturz bemerkt aufgetreten, nachdem diese Ausnahme ausgelöst wurde. Anscheinend funktioniert Qt nicht gut mit Ausnahmen, so dass dies nicht behandelt wurde und die Nachricht stillschweigend verworfen wurde, aber die Ausnahme stürzte die Anwendung trotzdem ab. Der Grund dafür ist, dass VS das Arbeitsverzeichnis für eine Debug-Binärdatei im Projektverzeichnis und nicht im Ausgabeverzeichnis festgelegt hat. Ich hatte eine Kopie von meinem Wörterbücher/Verzeichnis in den Ausgabeverzeichnissen "Debug" und "Release" nur, so dass die Dateien nicht gefunden werden konnten, die den Absturz verursacht haben. Wenn sie von den Verzeichnissen "Debug" oder "Release" ausgeführt wurden, wurden sie korrekt geöffnet und nichts passierte.

Ich habe eine Kopie der Wörterbücher/Verzeichnis im Projektverzeichnis auch und es lief unter dem Debugger ohne ein Problem. Darüber hinaus habe ich alle Ausnahmen, die Direktiven aus meinem Code entfernen, entfernt, um Probleme in der Zukunft zu vermeiden (sie scheinen in Qt-Anwendungen nicht gut zu funktionieren). Ich mache das nicht eine akzeptierte Antwort aber, weil ich immer noch wissen möchte:

  1. Warum nicht die von dem Parser geworfen Ausnahme irgendwo hängen bleiben (main(), Main :: Mainform()) ? Hat Qt das gemacht und wenn ja, wie? Können Sie Ausnahmen in Multithread-Code trotzdem verwenden?
    Edit: antwort: Ich habe gerade herausgefunden, Ausnahmen können Thread Grenzen nicht überschreiten. Meine try-catch-Direktiven wurden in main um das ganze Konstrukt platziert und in den MainForm-Konstruktor, wo ich SetupThread :: run() aufgerufen habe. Wenn ich einen try-catch-Block in run() selbst setzte, wurde der Inhalt des catch-Blocks korrekt ausgeführt, wenn eine Ausnahme ausgelöst wurde.
  2. Warum habe ich eine so verwirrende Stack-Trace-Ausgabe bekommen, die mich auf eine wilde Jagd geführt hat? Es sah so aus, als gäbe es Probleme beim Erstellen eines QString aus einem const char * und/oder einem malloc irgendwo. Ich habe viele "interessante" Theorien über die wahrscheinlichen Ursachen solcher Fehler entwickelt, bevor ich herausgefunden habe, was das wirklich verursacht hat ...
    Eigentlich waren die Stack-Spuren aus der debug-symbols-freien Version nun im Nachhinein sinnvoller: Es gab Referenzen auf sowas wie _CxxExceptionThrown darin und keine Verweise auf entweder malloc oder QString, aber ich nahm einfach an, es fehlten die Infos zum anzeigen Ursprung.
+1

Ausnahmen können nur von einem Stackframe auf dem Stack abgefangen werden. In deinem Thread ist main() nicht auf dem Stackframe des Threads. –

+0

Ich bin mir nicht sicher, ob du das gleiche meintest, worüber ich in meinem Edit geschrieben habe, aber ich bin + 1 du nur um sicher zu sein. ;) – neuviemeporte

Verwandte Themen