Russian Qt Forum

Qt => Многопоточное программирование, процессы => Тема начата: xaleva от Февраль 15, 2011, 12:59



Название: Некорректное поведение многопоточной программы
Отправлено: xaleva от Февраль 15, 2011, 12:59
Добрый день.Кто нибудь сталкивался с такой проблемой: многпоточное сокетное приложение,временами начинает некорректно работать.Сообщения получает не правильно(обрезает,причем не из за того что превышается длина,а просто вместо 2 символов получает один),а иногда зацикливается(это можно увидеть по логированию).Вопрос,могут ли не правильно используемые мьютексы привести к такому?При этом циклы не используются естественно.


Название: Re: Некорректное поведение многопоточной программы
Отправлено: Igors от Февраль 15, 2011, 20:07
Вопрос,могут ли не правильно используемые мьютексы привести к такому?При этом циклы не используются естественно.
Конечно могут, но это также может быть все что угодно - пока по Вашему общему описанию ничего конкретного сказать нельзя.


Название: Re: Некорректное поведение многопоточной программы
Отправлено: xaleva от Февраль 15, 2011, 20:32
Ок, тогда приведу общую картину.В главном потоке создаются потоки:

  TerminalThread *_thread = new TerminalThread (*_terminalList[number]);
    connect (_thread,SIGNAL (signalSocketConnected (QString)),this,SLOT (slotSocketConnected (QString)),Qt::QueuedConnection);
 и т.д....

в TerminalThread::run следующий код:
void TerminalThread::run ()
{
    Terminal _terminal (_terminalObject);
    _terminal.moveToThread (this);
    connect (&_terminal,SIGNAL (signalSocketConnected (QString)),this,SLOT (slotSocketConnected (QString)),Qt::QueuedConnection);
   ...и т.д

    _terminal.tryConnection ();
    exec ();
    disconnect (&_terminal,SIGNAL (signalSocketConnected (QString)),this,SLOT (slotSocketConnected (QString)));
... и т.д
}


Terminal.cpp:

Terminal::Terminal (TerminalObject& terminal)
{
    _socket = new QTcpSocket (this);
    _timer = new QTimer (this);
    connect (_timer,SIGNAL (timeout ()),this,SLOT (slotConnectionTimer ()));
    ...
}

void Terminal::slotSocketReadyRead ()
{
    QTextStream stream (_socket);
    QString value = stream.readAll ();
    checkSignal (value);
}

void Terminal::checkSignal (QString value)
{
      .....
    _log.writeLog (val);
}

Вот здесь переменная _log это singletone.Внутри синглтона и внутри TerminalObject все функции начинаются с записи QMutexLocker locker (&_mutex);
Т.е в принципе,все сигналы типа QueuedConnection, значит блокирование должно происходить только после завершения eventLoop, т.е блокирование не происходит.Все синглтоны вроде обезопасены записью QMutexLocker locker (&_mutex);
Есть ли ошибки в проектировании или я правильно все синхронизировал.


Название: Re: Некорректное поведение многопоточной программы
Отправлено: xaleva от Февраль 16, 2011, 09:53
    QTextStream stream (_socket);
    QString value = stream.readAll ();

Такой еще вопрос: readAll работает всегда правильно?


Название: Re: Некорректное поведение многопоточной программы
Отправлено: BRE от Февраль 16, 2011, 10:05
readAll работает всегда правильно?
Ну в общем то да.  :)
Эта конструкция вычитывает из сокета все, что пришло на текущий момент.


Название: Re: Некорректное поведение многопоточной программы
Отправлено: kdm от Февраль 16, 2011, 10:10
В примерах к какой-то книги есть такой момент, что в пакет сообщения в начало вставляется длина пакета в байтах (sizeof). В клиенте действительно принимается все что пришло именно на текущий момент и производится проверка пришел ли весь пакет, если нет то "обрабатывающий" класс ждет дальше. Просто в памяти вспомнился такой момент, не мог понять почему так и нет автоматических средств для таких случаев. Книгу сейчас не помню, но если что посмотрю, где-то валяется с примерами.
(ЗЫ может где-то в терминах неправильно выразился)


Название: Re: Некорректное поведение многопоточной программы
Отправлено: xaleva от Февраль 16, 2011, 10:14
Тогда ничего не понятно  :) Никак не могу найти,в чем проблема.При тестировании ничего не вылезает.Но в реальных условиях,при 5-6 подключениях вдруг творится чтот не понятное.Ок, тогда:
1. То,что сигналы от главного потока и обратно соединены типом QueuedConnection может как то повлиять?
2. То,что я в главном создаю синглтоны для работы с логированием и базой данных а потом в дочерних беру их instance и использую,причем все методы внутри синглтона защищены QMutexLocker _locker (&_mutex);, может как то повлиять?




Название: Re: Некорректное поведение многопоточной программы
Отправлено: xaleva от Февраль 16, 2011, 10:16
Да, это используется,если я побайтово принимаю сообщение.Я не стал это делать,потому что у меня все сообщения текстовые, 2 символа и конец строки.Иногда они склеиваются почему то,но все равно есть возможность split ом разделить по \t или \n


Название: Re: Некорректное поведение многопоточной программы
Отправлено: BRE от Февраль 16, 2011, 10:19
не мог понять почему так и нет автоматических средств для таких случаев.
Наверное потому, что это только один из вариантов контроля потока с данными. Есть и другие, для примера можно вспомнить такие протоколы как http, ftp.


Название: Re: Некорректное поведение многопоточной программы
Отправлено: BRE от Февраль 16, 2011, 10:33
После всего написанного, тебя не смущает твой слот?  ;)
Код
C++ (Qt)
void Terminal::slotSocketReadyRead ()
{
   QTextStream stream (_socket);
   QString value = stream.readAll ();
   checkSignal (value);
}
 

Что будет, если этот слот вызовется когда придет только половина сообщения или полтора сообщения?


Название: Re: Некорректное поведение многопоточной программы
Отправлено: xaleva от Февраль 16, 2011, 10:40
 :) а что будет если придет половина сообщения? Он считает половину, запишет,потом через некоторое время придет другая половина.Проблема в том что пол логам выходит что из двух символов сообщения приходит второй,а первый вообще никак не отмечается в логах,поэтому момент,что сообщение обрезается на две части я отбросил


Название: Re: Некорректное поведение многопоточной программы
Отправлено: kdm от Февраль 16, 2011, 10:42
:) а что будет если придет половина сообщения? Он считает половину, запишет,потом через некоторое время придет другая половина.Проблема в том что пол логам выходит что из двух символов сообщения приходит второй,а первый вообще никак не отмечается в логах,поэтому момент,что сообщение обрезается на две части я отбросил
Может лучше обратится к стандартным примерам. Тем более если только текстовые сообщения?
Выложил бы для интереса других людей проект в сообщении (если не корпоративная тайна))), а то не пойму зачем таймеры, мутексы...
Пример что я видел достаточно прост и без этих хитростей.


Название: Re: Некорректное поведение многопоточной программы
Отправлено: BRE от Февраль 16, 2011, 10:49
,поэтому момент,что сообщение обрезается на две части я отбросил
Ну и напрасно. Стек tcp гарантирует целостность всех передаваемых данных, т.е. он не может "глотать" какие-то данные и молча отдавать тебе следующие.  :)

Единственное объяснение, это первый символ сообщения просто "прилипает" к предыдущему сообщению.


Название: Re: Некорректное поведение многопоточной программы
Отправлено: xaleva от Февраль 16, 2011, 10:55
Да, а предыдущее тоже ведь записывается с лог, я бы заметил это.Да,и еще забыл уточнить, сообщения обычно идут максимум 1 в сек.А во время сбоя в секунду по 10 раз этот один символ...как будто зацикливается..


Название: Re: Некорректное поведение многопоточной программы
Отправлено: BRE от Февраль 16, 2011, 11:42
Да, а предыдущее тоже ведь записывается с лог, я бы заметил это.Да,и еще забыл уточнить, сообщения обычно идут максимум 1 в сек.А во время сбоя в секунду по 10 раз этот один символ...как будто зацикливается..
Ну тут сложно что-то сказать, что там где зацикливается, попробуй вставить отладочный ввод в сам слот приема и посмотри что он читает:
Код
C++ (Qt)
void Terminal::slotSocketReadyRead ()
{
QByteArray data = _socket.readAll();
qDebug() << data.toHex();
checkSignal( data );
}
 


Название: Re: Некорректное поведение многопоточной программы
Отправлено: xaleva от Февраль 16, 2011, 12:29
Да, я как то ставил кажется такой блог. Он выдает тоже самое.По правде говоря момент,что readAll не правильно работает меня волнует,но не так сильно.Главное узнать,не происходит ли зацикливание в следствие не правильно организованной работы с потоками. Но учитывая,что та часть кода ни у кого нареканий не вызывает,как и у меня,думаю надо искать причину в чем то другом...


Название: Re: Некорректное поведение многопоточной программы
Отправлено: BRE от Февраль 16, 2011, 12:43
Но учитывая,что та часть кода ни у кого нареканий не вызывает,как и у меня,думаю надо искать причину в чем то другом...
Ну не знаю про какую часть кода идет речь, но если про ту часть, что в ответе # 2, то возникает вопрос:
Перетаскивается ли объект класса TerminalThread в контекст этого потока и в продолжении его...
Проверял ли ты в контексте какого потока выполняется слот slotSocketConnected?


Название: Re: Некорректное поведение многопоточной программы
Отправлено: xaleva от Февраль 16, 2011, 13:02
Цитировать
1.Перетаскивается ли объект класса TerminalThread в контекст этого потока и в продолжении его...

Да, вообще,если не ошибаюсь все объекты,созданные в методе run автоматически пренадлежат этому потоку
Код:
TerminalThread::TerminalThread (TerminalObject& terminal) :
        QThread (),
        _terminalObject (terminal)
{
    moveToThread (this);
}
void TerminalThread::run ()
{
    Terminal _terminal (_terminalObject);
    _terminal.moveToThread (this);
...
Как видно,я на всякий случай даже вставил moveToThread.

Цитировать
Проверял ли ты в контексте какого потока выполняется слот slotSocketConnected?

Да, путем вывода во всех функциях Terminal qDebug () << thread()->currentThreadId();
id не совпадал естестно с айди главного потока....вот,поэтому даже не знаю что и предположить.


Название: Re: Некорректное поведение многопоточной программы
Отправлено: BRE от Февраль 16, 2011, 13:13
Да, вообще,если не ошибаюсь все объекты,созданные в методе run автоматически пренадлежат этому потоку
Те что созданы в run принадлежат этому потоку, но сам объект класса TerminalThread будет принадлежать тому потоку в котором он создавался (например, главному), пока не перенесешь его методом moveToThread. Для правильного соединения сигнал-слот это важно.

Нужно попытаться отследить всю цепочку данных: от слота приема и до последнего получателя, контролируя целостность данных. Добавь во все методы-участники этого обмена qDebug и смотри на каком этапе что теряется.


Название: Re: Некорректное поведение многопоточной программы
Отправлено: xaleva от Февраль 16, 2011, 17:12
Код:

TerminalThread::TerminalThread (TerminalObject& terminal) :
        QThread (),
        _terminalObject (terminal)
{
    qDebug () << "New thread" << thread()->currentThreadId();
    moveToThread (this);
    qDebug () << "New thread2" << thread()->currentThreadId();
}



Запись New thread и New thread2 выводят у всех потоков одну и ту же цифру.Значит ли это что сам объект класса TerminalThread не принадлежит созданному потоку?Если да, то что я должен для этого сделать?


Название: Re: Некорректное поведение многопоточной программы
Отправлено: BRE от Февраль 16, 2011, 18:01
Значит ли это что сам объект класса TerminalThread не принадлежит созданному потоку?Если да, то что я должен для этого сделать?
Все потоки работают в одном адресном пространстве, поэтому вся память (все объекты) у них общие.
Само понятие контекст потока помогает Qt правильно выполнить подключение сигналов и слотов. Ничего никуда реально не переноситься.
После вызова moveToThread для объекта TerminalThread, все сигналы от объектов из других потоков будут приходить через его очередь сообщений.

Цифры одинаковы потому, что поток реально формируется только во время его запуска (вызова QThread::start), а не при создании объекта QThread.


Название: Re: Некорректное поведение многопоточной программы
Отправлено: xaleva от Февраль 16, 2011, 19:21
 :) да, знаю.Подобные мысли приходят только от безисходности.


Название: Re: Некорректное поведение многопоточной программы
Отправлено: BRE от Февраль 16, 2011, 19:34
:) да, знаю.Подобные мысли приходят только от безисходности.
Попробуй сделать новый тестовый проект, убери все потоки, добейся что-бы все работало, а потом уже добавь потоки.
Что либо телепатически предполагать совсем не хочется.  :)


Название: Re: Некорректное поведение многопоточной программы
Отправлено: vunder от Февраль 23, 2011, 21:52
Код:

TerminalThread::TerminalThread (TerminalObject& terminal) :
        QThread (),
        _terminalObject (terminal)
{
    qDebug () << "New thread" << thread()->currentThreadId();
    moveToThread (this);
    qDebug () << "New thread2" << thread()->currentThreadId();
}



Запись New thread и New thread2 выводят у всех потоков одну и ту же цифру.Значит ли это что сам объект класса TerminalThread не принадлежит созданному потоку?Если да, то что я должен для этого сделать?
Они и будут одинаковыми. Конструктор то вызывается в контексте главного потока. Вызов moveToThread() говорит о том, что объект будет работать в другом потоке, но не значит, что он сиюминутно туда перемещен. Выполняемый метод, т.е. конструктор в данном случае, все равно закончит выполнение в контексте процесса, в котором он был вызван