Russian Qt Forum
Ноябрь 22, 2024, 23:17 *
Добро пожаловать, Гость. Пожалуйста, войдите или зарегистрируйтесь.
Вам не пришло письмо с кодом активации?

Войти
 
  Начало   Форум  WIKI (Вики)FAQ Помощь Поиск Войти Регистрация  

Страниц: [1]   Вниз
  Печать  
Автор Тема: Работа с dll во втором потоке  (Прочитано 8954 раз)
vaychick
Гость
« : Май 23, 2011, 16:26 »

Здравствуйте, есть ф-я которая в цикле вызывает ф-и динамически подгружаемой dll, задача была вынести эту ф-ю в отдельный поток - вынес, но есть проблема при обращении к функциям dll приложение вылетает с SIGSERV - Segmentation fault. Причем если убрать вызовы этих dll ф-й все работает нормально.

Собственно вопрос, какие есть подводные камни при использовании dll во втором потоке, 2 дня бъюсь и никак не могу найти причину вылета программы. Причем без вызова dll поток нормально стартует, работает и завершается.

Та же самая dll с теми-же ф-ми работала стабильно в главном потоке.
Реализовал второй поток след. образом: Внутри наследника QThread, создается объект который работает с dll, он переносится во второй поток методом moveToThread, затем стартует цикл обработки событий, а ф-я которая крутит цикл, это слот, который вызывается после посылкой сигнала извне.

исходный код не привожу, так как из контекста его трудно достать, если нужно будет выложу.
Могу кратко описать логику в псевдокоде

Код:
class thread : public QThread {
    Worker * worker;
public:
    void init() {
         worker = new Worker;
     }
    void start() {
        worker->moveToThread(this);
        QThread::start();
    }
    void run() {
        exec();
    }
};

class Worker : public QObject {
    someMember;
    someMember2;
    someMember3;
    typedef dllFoo * dllFooProto;
    QLibrary dll;
public:
    Worker() {
    dll.load();
    dllFoo = (dllFooProto)dll.resolve("dllFoo");
}
public slots:
    void foo() {
        while(1) {
            QCoreApplication::processEvents();
            if(isExit) return;//Внешнее событие устанавливает флаг для выхода из цикла
            Sleep(1000);
           
            dllFoo(someMember, &someMember2, &someMember3);//Ф-я модифицирует содержимое someMember2 и someMember3
        }
    }
};

dll пробовал грузить и в конструкторе (main thread) и внутри foo()

если закоментировать вызов dllFoo и самому модифицировать someMember-ры, то все нормально, создается впечатление что dll что-то внутри себя делает, что конфликтует с потоками, пробовал обрамлять вызов dllFoo в mutex.lock() \ unlock(), эффекта нет.

Примечательно то, что ф-и из dll не используются в основном потоке, и свойства someMember так-же не используются, мотивов для блокировок я не вижу, приложение просто вылетает после вызова ф-й dll. Причем dll обращается к внешнему устройству и более никак не связано с приложением.

читал тут http://habrahabr.ru/blogs/qt_software/115830/ и сдесь на форуме и документацию Qt, поэтому кажеться какбудто поток организован верно, хотя я могу ошибаться.
Кажется будто причина в dll, такое может быть? если да, то какие меры можно предпренять?
« Последнее редактирование: Май 23, 2011, 16:34 от vaychick » Записан
twp
Гость
« Ответ #1 : Май 23, 2011, 17:14 »

по ходу QLibrary создается в главном потоке и не переносится (как Worker) в рабочий поток
Записан
xokc
Птица говорун
*****
Offline Offline

Сообщений: 976



Просмотр профиля
« Ответ #2 : Май 23, 2011, 20:14 »

Честно говоря ни разу не имел проблем с загрузкой DLL из не основного потока. Видимо проблема именно в Вашей dll.
Записан
vaychick
Гость
« Ответ #3 : Май 24, 2011, 08:13 »

по ходу QLibrary создается в главном потоке и не переносится (как Worker) в рабочий поток

Но ведь она часть объекта Worker. Я пробовал и другой вариант, непосредственно в foo() подгружать dll:

void foo() {
    QLibrary dll2("dllPath");
    dll2.load();
    if(!(dllFoo = (dllFooProto)dll2.resolve("dllFoo")))
        qDebug() << "error";

        while(1) {
            QCoreApplication::processEvents();
            if(isExit) return;//Внешнее событие устанавливает флаг для выхода из цикла
            Sleep(1000);
           
            dllFoo(someMember, &someMember2, &someMember3);//Ф-я модифицирует содержимое someMember2 и someMember3
        }
    }

добавлю так-же что Worker не имеет предка, но наследуется от абстрактного класса, поэтому мне кажется, что должно переноситься нормально методом moveToThread

Цитировать
Честно говоря ни разу не имел проблем с загрузкой DLL из не основного потока. Видимо проблема именно в Вашей dll.
Тоже начинаю об этом думать, но dllFoo просто обращается к устройству и считывает из него поток байтов в someMember, какие коллизии могут создаться из-за многопоточности не понимаю
« Последнее редактирование: Май 24, 2011, 08:16 от vaychick » Записан
twp
Гость
« Ответ #4 : Май 24, 2011, 09:33 »

по ходу QLibrary создается в главном потоке и не переносится (как Worker) в рабочий поток

Но ведь она часть объекта Worker. Я пробовал и другой вариант, непосредственно в foo() подгружать dll:

void foo() {
    QLibrary dll2("dllPath");
    dll2.load();
    if(!(dllFoo = (dllFooProto)dll2.resolve("dllFoo")))
        qDebug() << "error";

        while(1) {
            QCoreApplication::processEvents();
            if(isExit) return;//Внешнее событие устанавливает флаг для выхода из цикла
            Sleep(1000);
           
            dllFoo(someMember, &someMember2, &someMember3);//Ф-я модифицирует содержимое someMember2 и someMember3
        }
    }

добавлю так-же что Worker не имеет предка, но наследуется от абстрактного класса, поэтому мне кажется, что должно переноситься нормально методом moveToThread
согласно документации moveToThread переносит объект и его детей, но поскольку в конструктор QLibrary не был передан Worker как родитель, то dll не был перенесен в поток вместе с Worker и остался в главной потоке. Кроме того, в потоке используется Sleep. Может проблема не в нем, но не понятно почему не используется QThread::msleep, QThread::sleep или QThread::usleep.
Записан
Igors
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 11445


Просмотр профиля
« Ответ #5 : Май 24, 2011, 09:42 »

Не вижу причем тут moveToThread - хоть бы и вообще ничего не переносили, слот должен работать. Разумеется Вы убедились что в главной нитке dllFoo работает нормально. Я бы начал интересоваться

- отключить весь остальной код слота (оставить только вызов dllFoo)
- какой адрес возвращает resolve (сравнить его с возвращаемым в главной)
- стеком краша - вошла ли она в dllFoo и где рухнула

Если это Вындоуз - убедиться что dll создана как "Multithreaded (Debug) DLL"  
Записан
SASA
Гость
« Ответ #6 : Май 24, 2011, 10:05 »

А когда происходит соединение со слотом void foo()? Может надо точно указать тип соединения?
Записан
vaychick
Гость
« Ответ #7 : Май 24, 2011, 13:36 »

Цитировать
согласно документации moveToThread переносит объект и его детей, но поскольку в конструктор QLibrary не был передан Worker как родитель, то dll не был перенесен в поток вместе с Worker и остался в главной потоке. Кроме того, в потоке используется Sleep. Может проблема не в нем, но не понятно почему не используется QThread::msleep, QThread::sleep или QThread::usleep.

Спасибо за информацию насчет переноса свойств объекта, но сейчас тестирую когда dll грузится прям в слоте.
Я не использовал QThread::sleep потому, что Worker не унаследован от QThread, и "не знает" что он свойство класса thread, поэтому использую системныю ф-ю sleep, без вызова dllFoo цикл крутиться нормально.

Цитировать
Не вижу причем тут moveToThread - хоть бы и вообще ничего не переносили, слот должен работать. Разумеется Вы убедились что в главной нитке dllFoo работает нормально.
Да, в главном потоке, точнее вообзе без потоков, работало нормально, пока не начал переделывать.

Цитировать
- отключить весь остальной код слота (оставить только вызов dllFoo)
- какой адрес возвращает resolve (сравнить его с возвращаемым в главной)
- стеком краша - вошла ли она в dllFoo и где рухнула

Реальный код, который сейчас тестирую
Код:
    void ReaderCOM::pollReader() {
    QLibrary dll2("Mr915ApiV10");
    dll2.load();
    if(!(gen2MultiTagIdentify = (APIGen2MultiTagIdentify)dll2.resolve("Gen2MultiTagIdentify")))
        qDebug() << "Gen2MultiTagIdentify";
    if(!(clearIDBuffer = (APIClearIDBuffer)dll2.resolve("ClearIDBuffer")))
        qDebug() << "ClearIDBuffer";

    while(1) {
        qDebug() << "Gen2MultiTagIdentify:" << QString::number((int)gen2MultiTagIdentify, 16);//Вот тут вылет на 3-й итерации
        clearIDBuffer(hReader, readerAddr);
        gen2MultiTagIdentify(hReader, &count, tags, readerAddr);
    }
}

вывод:

Код:
Gen2MultiTagIdentify: "1b621b50" 
Gen2MultiTagIdentify: "1b621b50"
Gen2MultiTagIdentify: "?"

"?" - наверное потому, что на третьей итерации вылетает
Отладчик показывает на это место SIGSERV

Код:
QTextStream &QTextStream::operator<<(const QString &string)
{
    Q_D(QTextStream);
    CHECK_VALID_STREAM(*this);//Вот тут
    d->putString(string);
    return *this;
}

я выводил адреса gen2MultiTagIdentify в конструкторе(основной поток) и в цикле, перед вызовом qDebug() << "Gen2MultiTagIdentify:", они совпадают
Притом вылет происходит на 3-й итерации цикла, а не на первой.
Притом если ничего ны выводить в лог - вылетает все равно
« Последнее редактирование: Май 24, 2011, 14:05 от vaychick » Записан
vaychick
Гость
« Ответ #8 : Май 24, 2011, 13:38 »

Код:
А когда происходит соединение со слотом void foo()? Может надо точно указать тип соединения?
слот вызывается 1 раз из основного потока сигналом, соединение - Qt::QueuedConnection, повторюсь, без ф-й dll все работает и не падает
Записан
Igors
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 11445


Просмотр профиля
« Ответ #9 : Май 24, 2011, 15:58 »

Для начала проверить на битую кучу (инструмент зависит от платформы/компилятора). Затем углубляться в изучение dll - в первую очередь какие сигналы она испускает, и могут ли они навредить если посланы из др. нитки
Записан
blood_shadow
Гость
« Ответ #10 : Май 24, 2011, 17:04 »

Для начала проверить на битую кучу (инструмент зависит от платформы/компилятора)
а с помощью mingw в среде Creator'а как такое сделать?
Записан
vaychick
Гость
« Ответ #11 : Май 25, 2011, 08:16 »

Цитировать
Для начала проверить на битую кучу (инструмент зависит от платформы/компилятора). Затем углубляться в изучение dll - в первую очередь какие сигналы она испускает, и могут ли они навредить если посланы из др. нитки

Я честно говоря не в курсе, что за сигналы в DLL, но хочу сказать что dll сторонняя и скомпилирована Microsoft Visual C++ 6.0 DLL, как показал PEid
Записан
Igors
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 11445


Просмотр профиля
« Ответ #12 : Май 25, 2011, 13:54 »

а с помощью mingw в среде Creator'а как такое сделать?
Никогда не пользовался этой IDE, поэтому здесь подсказать не могу.

Я честно говоря не в курсе, что за сигналы в DLL, но хочу сказать что dll сторонняя и скомпилирована Microsoft Visual C++ 6.0 DLL, как показал PEid
Надо убедиться что начиная от вызова ф-ции dll и до краша управление нигде не отдается Вам. Если таких callback'ов нет, то пора сделать вывод что это проблема конкретной dll и сосредлточиться на поиске ее новой версии (или переносе вызовов в главную нитку)
Записан
vaychick
Гость
« Ответ #13 : Май 25, 2011, 16:02 »

Цитировать
Надо убедиться что начиная от вызова ф-ции dll и до краша управление нигде не отдается Вам. Если таких callback'ов нет, то пора сделать вывод что это проблема конкретной dll и сосредлточиться на поиске ее новой версии (или переносе вызовов в главную нитку)
На данный момент приложение падает, через несколько инструкций после того, как вызов ф-и из dll отработает, dll весит 63 килобайта и работает с COM портом, начал задумываться о том, чтобы реализовать самому эти ф-и, но пока не могу найти точной спецификации
Записан
Страниц: [1]   Вверх
  Печать  
 
Перейти в:  


Страница сгенерирована за 0.073 секунд. Запросов: 23.