3

线程很新,我有线程在它们之间共享的这个 QList。他们都有自己的工作空间,GUI(模型/视图)不断地访问这个列表。然后我得到这个指向 QDataList.size() 的崩溃。调试并没有真正帮助我,因为如果我单步执行代码并且当我尝试崩溃的 qList 时,我从来没有遇到过这个问题,没有可用的信息。

所以,我的问题是:是否可以同时获取 Qlists 大小和读取对象?列表中的对象是线程安全的,不能同时被不同的线程读/写。

获取“0xC0000005:访问冲突读取位置 0xfeeefefa”。这指向我:inline int size() const in 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++

每个线程都有一个执行 networkRequest 的网络管理器:

QMutexLocker 中的 QThread ASSERT 失败:“QMutex 指针未对齐”,

4

2 回答 2

10

我意识到可以预先分配一个 QList 并让线程在该列表的不同区域上运行,但在我看来,我认为这是一个不好的模式。

当我使用 Qt 时(我实际上是使用 PyQt,因为我是一名 Python 程序员),我觉得最好利用提供给您的信号/插槽机制,并且永远不要在线程之间共享内存。每个线程都应该在创建时直接获得自己的数据,或者随着时间的推移通过等待的队列获得。当它完成它的工作或一大块工作时,它可以发出带有数据的信号。您将拥有一个连接到所有线程的处理程序,以侦听准备就绪的数据。

这种模式的结果是您没有共享内存,也不必太担心锁,而只是等待工作人员发出他们的数据准备好的信号,并且单个处理程序正在收集和更新主模型.

话虽如此,这里是另一个使用 QList 作为共享内存并经历崩溃直到他们锁定它的人的参考:http: //developer.qt.nokia.com/forums/viewthread/13049

我认为当人们(包括我自己)第一次开始使用线程时,最直接的冲动就是像往常一样使用容器。但是一旦你开始线程化,你已经立即增加了代码逻辑的复杂性以及错误的容量。共享内存的同步是实现它的一种方法,在访问之前使用互斥锁来锁定资源。但我认为值得一提的是另一种沟通方式。

谷歌 Go 语言的核心原则之一是:“不要通过共享内存进行通信;通过通信来共享内存” http://golang.org/doc/codewalk/sharemem/

Go 想要通过通道对象(类似于 Qt 中的信号/插槽)传递内存来解决其核心问题。一个组件具有对内存的独占本地访问权,然后通过通道将其传递给另一个组件。这保证你不会有比赛条件。无论如何,我只是认为我会附加这个额外的参考,因为我觉得它与线程编程问题非常相关。

于 2012-02-28T03:55:59.590 回答
3

不,他们不能出错,也许/可能。文档说所有 QList 函数都是唯一的reentrant,而不是线程安全的。现在,通常从共享结构中读取应该是线程安全的,但在这种情况下,Qt 有明确的文档,并且读取时没有例外。因此,您必须假设它是不允许的并且可能导致错误。

请注意, astd::list不会遇到此问题,并且允许从多个线程读取(写入当然仍然必须是独占的)。

注意Qt线程整体有很多特殊情况,有人提示我写“对线程API的要求”,其中很多Qt都失败了。这一部分是历史,一部分是处理各种硬件,但总的来说 Qt 线程必须特殊处理。


由于文档不一致(请参阅评论),我快速查看了 QList 源代码。像“begin”、“size”和“first”这样的简单函数似乎是只读的。他们不修改数据结构。因此,如果它们导致崩溃,那是因为其他一些线程同时正在修改列表。

于 2012-02-28T07:44:18.613 回答