Могут ли два потока читать из одного и того же QList одновременно? - PullRequest
3 голосов
/ 28 февраля 2012

Довольно плохо знаком с потоками, и у меня есть этот QList, который потоки разделяют между ними. Все они имеют свое собственное пространство, над которым они могут работать, и GUI (модель / представление) постоянно получает доступ к этому списку. Затем я получаю этот сбой, который указывает на QDataList.size (). Отладка на самом деле не помогает мне, так как я никогда не сталкиваюсь с этой проблемой, если перебираю код и когда пытаюсь понять, что такое qList, что приводит к сбою, информация недоступна.

Итак, мой вопрос: возможно ли получить размер Qlists и читать объекты одновременно? Объекты в списке являются потокобезопасными и не могут быть прочитаны / записаны разными потоками одновременно.

Получение «0xC0000005: нарушение прав доступа, местоположение чтения 0xfeeefefa.» что указывает мне на: встроенный int size () const в qlist.h

Я прошёл через стек вызовов и нашел это:

QtCored4.dll!QListData::size()  Line 98 + 0x11 bytes    C++
QtNetworkd4.dll!QList<enum QNetworkReplyImplPrivate::InternalNotifications>::size()  Line 137 + 0x10 bytes  C++
QtNetworkd4.dll!QNetworkReplyImplPrivate::resumeNotificationHandling()  Line 444 + 0xe bytes    C++
QtNetworkd4.dll!QNetworkReplyImplPrivate::finished()  Line 797  C++
QtNetworkd4.dll!QNetworkAccessBackend::finished()  Line 313 C++
QtNetworkd4.dll!QNetworkAccessHttpBackend::replyFinished()  Line 739    C++
QtNetworkd4.dll!QNetworkAccessHttpBackend::qt_static_metacall(QObject * _o, QMetaObject::Call _c, int _id, void * * _a)  Line 86 + 0x8 bytes    C++
QtCored4.dll!QMetaCallEvent::placeMetaCall(QObject * object)  Line 525 + 0x1d bytes C++
QtCored4.dll!QObject::event(QEvent * e)  Line 1195 + 0x14 bytes C++
QtGuid4.dll!QApplicationPrivate::notify_helper(QObject * receiver, QEvent * e)  Line 4550 + 0x11 bytes  C++
QtGuid4.dll!QApplication::notify(QObject * receiver, QEvent * e)  Line 3932 + 0x10 bytes    C++
QtCored4.dll!QCoreApplication::notifyInternal(QObject * receiver, QEvent * event)  Line 876 + 0x15 bytes    C++
QtCored4.dll!QCoreApplication::sendEvent(QObject * receiver, QEvent * event)  Line 231 + 0x39 bytes C++
QtCored4.dll!QCoreApplicationPrivate::sendPostedEvents(QObject * receiver, int event_type, QThreadData * data)  Line 1500 + 0xd bytes   C++
QtCored4.dll!qt_internal_proc(HWND__ * hwnd, unsigned int message, unsigned int wp, long lp)  Line 496 + 0x10 bytes C++

В каждом потоке есть сетевой менеджер, выполняющий запрос сети:

Ошибка QThread ASSERT в QMutexLocker: «Указатель QMutex смещен»,

Ответы [ 2 ]

10 голосов
/ 28 февраля 2012

Я понимаю, что возможно предварительно выделить QList и позволить потокам работать в разных областях этого списка, но, на мой взгляд, я считаю, что это плохой шаблон для подражания.

Когда я работаю с Qt (на самом деле я работаю с PyQt, так как я программист на python), я чувствую, что лучше всего использовать предоставленный вам механизм сигнал / слот и никогда не делю память между потоками. Каждому потоку должны быть предоставлены свои собственные данные либо непосредственно при создании, либо со временем через очередь, которая ожидает. Когда это сделано с его работой или частью работы, это может испустить сигнал с данными. У вас будет один обработчик, который соединяется со всеми вашими потоками для прослушивания данных, которые будут готовы.

Результатом этого паттерна является то, что вы не делите память и вам не нужно слишком беспокоиться о блокировках, а просто ждите, когда рабочие сообщат, что их данные готовы, и один обработчик собирает и обновляет основная модель.

При этом здесь приведено еще одно упоминание о том, что кто-то использует QList в качестве разделяемой памяти и испытывает сбои до тех пор, пока не заблокирует его: http://developer.qt.nokia.com/forums/viewthread/13049

Я думаю, что когда люди (включая меня) впервые начинают работать с потоком, немедленный импульс заключается в том, чтобы просто использовать контейнеры так, как они это делают всегда. Но как только вы начнете работу с потоками, вы сразу же увеличите сложность логики кода и количество ошибок. Синхронизация разделяемой памяти - один из способов приблизиться к ней, используя мьютексы для блокировки ресурсов перед доступом. Но я думаю, что стоит упомянуть другой способ общения.

Язык Googles Go был построен с одним из основных принципов: «Не общайтесь, разделяя память; делитесь памятью, общаясь» http://golang.org/doc/codewalk/sharemem/

Go хотел решить эту проблему по своей сути, передавая память через объекты каналов, которые напоминают сигнал / слот в Qt. Один компонент имеет эксклюзивный локальный доступ к памяти, а затем передает его другому компоненту по каналу. Это гарантирует, что у вас не будет гоночных условий. В любом случае, я подумал, что я воспользуюсь этой дополнительной ссылкой, потому что я чувствую, что она очень важна для проблем программирования потоков.

3 голосов
/ 28 февраля 2012

Нет, они не могут Ошибаться, возможно / возможно.В документации сказано, что все функции QList являются только reentrant и не поточнобезопасными.Теперь обычное чтение из разделяемой структуры должно быть поточно-ориентированным, но в этом случае Qt имеет явную документацию и не делается исключения для чтения.Поэтому вы должны предположить, что это недопустимо и может привести к ошибкам.

Обратите внимание, что std::list не страдает от этой проблемы и допускает чтение из нескольких потоков (запись, конечно, все же должна быть исключительной).

Обратите внимание, что у многопоточности Qt в целом много особых случаев, мне было предложено написать " требования для многопоточного API ", во многих из которых Qt не работает.Отчасти это история, а отчасти это связано с различными аппаратными средствами, но в общем случае с потоками в Qt нужно обращаться особенно.


Поскольку документы не согласованы (см. Комментарий), я сделалкраткий обзор исходного кода QList.Простые функции, такие как «начало», «размер» и «первый», действительно доступны только для чтения.Они не изменяют структуру данных.Таким образом, если они вызывают сбой, это происходит потому, что какой-то другой поток изменяет список одновременно.

...