Странные ошибки во время выполнения с многопоточным приложением Q ++ Qt, присутствующим только в отладчике VS - PullRequest
1 голос
/ 07 декабря 2010

Я пишу небольшое хобби-приложение на C ++ с использованием Qt.При запуске приложения необходимо прочитать некоторые файлы словарей, что занимает некоторое время, поэтому я создал собственный класс потока для анализа словарей в фоновом режиме:

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;
};

Три класса «Словарь» созданы мной ивсе они наследуют общий интерфейс, который включает в себя функциональность Q_OBJECT, чтобы сигнализировать основному классу через заголовок потока установки в режиме Qt :: QueuedConnection с обновлениями прогресса во время синтаксического анализа, поэтому он может отображать индикатор выполнения.Поток установки вызывается из конструктора основных классов следующим образом:

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();
}

У меня возникли некоторые проблемы с аварийным завершением приложения при выходе, и я не мог понять, в чем проблема, поэтому я попытался запустить егов отладчике Visual C ++ 2008.Затем я получаю огромный сбой при запуске:

Необработанное исключение в 0x7568b727 в kanjiflash.exe: исключение Microsoft C ++: std :: исключение в ячейке памяти 0x024ffa1c ..

Трассировка стека показывает:

KernelBase.dll! 7568b727 ()
[Кадры ниже могут быть неправильными и / или отсутствующими, символы не загружены для KernelBase.dll]
KernelBase.dll! 7568b727 ()
msvcr90d.dll! _heap_alloc_dbg_impl (unsigned int nSize = 72, int nBlockUse = 1, const char * szFileName = 0x00000000, int nLine = 0, int * errno_tmp = 0x024ff7 + 0 * 0 + 0 + 0) + 0 + 0 + 0 + 0 + C + 0) + 8 + 0 + 0 + 0 C ++ C ++ C ++ 8) C ++) 81019 * msvcr90d.dll! _Nh_malloc_dbg_impl (без знака int nSize = 72, int nhFlag = 0, int nBlockUse = 1, const char * szFileName = 0x00000000, int nLine = 0, int * errno_tmp = 0x024ff9 + 8 * 10 192 0 ++ 10 10 0 + 219 0 8 0 8 9 0 8 9 0 8 9 0 8 9 0 8 9 0 8 9 0 8 9 0 8 9 0 8 9 0 8 9 0 8 8 9 8 8 8 8 8 8 8 8 8 8 8 8 8 0 8 8 8 8 8 8 8 8 8 8 8 8 0 8 8 8 8 строка на линии связи 0 0 0 0 0 232 0 10 0 0 239 09 239 0 8 8 0 10 19 на линии строк* msvcr90d.dll! _nh_malloc_dbg (без знака int nSize = 72, int nhFlag = 0, int nBlockUse = 1, const char * szFileName = 0x00000000, int nLine = 0) Строка 296 + 0x1d байт C ++
msvcr90d.dll!unsigned int nSize = 1) Строка 56 + 0x15 bytes C ++
020bea68 ()
kanjiflash.exe! SetupThread :: run () Строка 391 + 0x2c байт C ++
QtCored4.dll! QThreadPrivate :: start (void * arg = 0x020bd0c8) Строка 317 C ++
msvcr90d.dll! _callthreadstartex () Строка 348 + 0xf байт C
msvcr90d.dll! _threadstartex (void * ptd = 0x020bd8f0) Строка 331 C
kernel32.dll! 75593677 ()
ntdll.dll! 77739d72()
ntdll.dll! 77739d45 ()

В этой строке указана конкретная строка в SetupThread :: run (), где я пытаюсь запустить parseDictionary ("..."),Этот стек вызовов получается из-под Windows7 64bit.В Windows XP 32bit у меня была идентичная проблема, единственное отличие заключалось в том, что стек шел в конструктор QString (const char * ch) из SetupThread :: run (), где он жаловался и показывал, что буфер * ch равен несколькимсимволы мусора.

Теперь странно то, что это происходит только внутри отладчика.Конфигурации Debug и Release отлично работают вне отладчика.Возившись с приложением, исследующим это, я обнаружил и исправил ошибку, из-за которой я сначала использовал отладчик, но мне интересно, что на самом деле происходит и что я буду делать, если мне когда-нибудь понадобится отладчик.Поскольку я не совсем разбираюсь в многопоточном программировании, я не уверен, возможно ли вообще запустить их в отладчике, или, возможно, я делаю что-то не так, например, работаю над указателями основного класса в рабочем потоке(нарушение доступа?).Любое понимание высоко ценится.

1 Ответ

1 голос
/ 07 декабря 2010

Я наткнулся на ответ сегодня, пока шел по программе в отладчике. Основываясь на этой трассировке стека, которую я получил раньше:

kernel32.dll! 7d4e2366 ()
[Указанные ниже кадры могут быть неправильными и / или отсутствующими, символы не загружены для kernel32.dll]
kernel32.dll! 7d4e2366 ()
QtCored4.dll! QString :: QString (const char * ch = 0x0265ff24) Строка 427 + 0x12 байт C ++
KanjiFlash.exe! SetupThread :: run () Строка 400 + 0x2c байт C ++
QtCored4.dll! QThreadPrivate :: start (void * arg = 0x02137f68) Строка 317 C ++
msvcr90d.dll! _callthreadstartex () Строка 348 + 0xf байт C
msvcr90d.dll! _threadstartex (void * ptd = 0x02138828) Строка 331 C
kernel32.dll! 7d4dfe21 ()

... и, увидев, что буфер const char * ch содержит мусор после сбоя, я предположил, что строковый литерал каким-то образом поврежден, и это стало причиной исключения. Но я попытался войти в первую функцию parseDictionary (), и это сработало. Начало функции выглядит так:

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");
    }
    /* ... */

Я заметил, что сбой произошел после того, как было сгенерировано это исключение. Очевидно, Qt не очень хорошо согласуется с исключениями, так что это осталось необработанным, и сообщение было молча отброшено, но исключение все равно привело к сбою приложения. Причина, по которой это произошло внутри отладчика, заключалась в том, что VS устанавливает рабочий каталог для отлаженного двоичного файла в каталоге проекта, а не в выходном каталоге. У меня была копия моего словаря / каталога в выходных каталогах «Отладка» и «Выпуск», поэтому не удалось найти файлы, которые вызвали сбой. При запуске из каталогов «Debug» или «Release» они открывались правильно и ничего не происходило.

Я сделал копию каталога dictionaries / в каталоге проекта, и он без проблем работал под отладчиком. Более того, я удалил все директивы, генерирующие исключения, из моего кода, чтобы избежать проблем в будущем (кажется, они не очень хорошо работают в приложениях Qt). Я не делаю это принятым ответом, однако, потому что я все еще хотел бы знать:

  1. Почему исключение, генерируемое анализатором, нигде не было поймано (main (), MainForm :: MainForm ())? Qt сделал это и если да, то как? Можно ли в любом случае использовать исключения в многопоточном коде?
    Редактировать: answer: Я только что обнаружил, что исключения не могут пересекать границы потоков. Мои директивы try-catch были помещены в main вокруг всего устройства и внутри конструктора MainForm, где я вызвал SetupThread :: run (). Когда я поместил блок try-catch в саму run (), содержимое блока catch было правильно выполнено при возникновении исключения.
  2. Почему я получил такой запутанный вывод трассировки стека, ведя меня в погоню за диким гусем? Похоже, были проблемы с созданием QString из const char * и / или где-то с malloc. Я разработал много «интересных» теорий о вероятных причинах таких ошибок, прежде чем я выяснил, что на самом деле вызвало это ...
    На самом деле, трассировка стека от выпуска без отладочных символов теперь стала более понятной: ссылки на что-то вроде _CxxExceptionThrown в нем и никаких ссылок ни на malloc, ни на QString, но я просто предположил, что ему не хватает информации, чтобы показать источник.
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...