简体   繁体   English

设计模式,Qt模型/视图和多线程

[英]Design Pattern, Qt Model/View and multiple threads

I am creating an application which displays the market data and uses it in some other forms too. 我正在创建一个显示市场数据的应用程序,并以其他形式使用它。 I store market data in a map say std::map<tickerId, StockData> . 我将市场数据存储在地图中,例如std::map<tickerId, StockData> Let me give one used case of how this map can be used. 让我举一个关于如何使用这个地图的例子。

  1. network sends a data packet encapsulating the stock Data at time t. 网络在时间t发送封装库存数据的数据包。 updatePrice(tickerId, latestPrice)
  2. update the stock data in the map. 更新地图中的库存数据。 Now, multiple threads can access/update the data. 现在,多个线程可以访问/更新数据。 So the map has to be locked for thread-safe operations. 因此必须锁定映射以进行线程安全操作。 Here is the first question, do I need to lock the underlying data too for updates? 这是第一个问题,我是否还需要锁定基础数据以进行更新?
  3. There are multiple uses of the new stock data, say, there is a price update on IBM, then I need to update the value of IBM in my portfolio. 新股票数据有多种用途,比如说IBM有价格更新,那么我需要在我的投资组合中更新IBM的价值。 As well as display the new data on a screen. 以及在屏幕上显示新数据。 And there can be several other simultaneous uses. 并且可以有其他几种同时使用。 updatePosition(tickerId, price) and updateStockScreen(tickerId, price) . updatePosition(tickerId, price)updateStockScreen(tickerId, price) Also, separting Gui updates from position update is important as GUI is not the main strength of the application. 此外,从位置更新中分离Gui更新非常重要,因为GUI不是应用程序的主要优势。
  4. I am just troubled about how to implement this type of design. 我对如何实现这种类型的设计感到困扰。 I read about Model/View Design in QT to display data but if View thread reads from the same map, it has to be locked. 我在QT中阅读了有关模型/视图设计的信息,但是如果View线程从同一个地图读取,则必须将其锁定。 This leads to an slow/inefficient design. 这导致设计缓慢/低效。 Every time view reads from the model, the model needs to be locked. 每次视图从模型中读取时,都需要锁定模型。 Is this preffered in real-time GUIs? 这是在实时GUI中提供的吗?
  5. To summarize, I have stored a lot of different objects as maps. 总而言之,我已经将许多不同的对象存储为地图。 And objects are updated in realtime. 对象实时更新。 I need to update them and then use them at various locations. 我需要更新它们,然后在不同的位置使用它们。 It would be great if someone can give me a small example on how to implement such designs. 如果有人能给我一个关于如何实现这种设计的小例子,那就太好了。

Some references to useful books are appreciated too. 对有用书籍的一些参考也受到赞赏。

I am new and trying to achieve too much with my little knowledge so forgive me if I have asked stupid/ill-formed questions. 我是新人,并试图通过我的小知识实现太多,所以如果我问过愚蠢/形成错误的问题,请原谅我。

Thanks Shiv 谢谢Shiv

It sounds conceptually like you want the model on one thread and the view on another, which I looked into at one point. 这听起来在概念上就像你想要一个线程上的模型和另一个线程上的视图,我在一个点上看到了。

If so...and your model is read-only through the view widget then yes, you have to lock. 如果是这样......并且您的模型通过视图小部件是只读的,那么是的,您必须锁定。 I'd argue that doing so undermines the elegance of the "decoupling" provided by the model/view separation. 我认为这样做会破坏模型/视图分离所提供的“解耦”的优雅。 But it could be made to work. 但它可以成功。

However...if your model is read-write through the view it's not possible to do correctly at all because of the queued nature of the notification slots. 但是...如果你的模型是通过它不可能做到正确的因为在所有的通知插槽排队性质的观点读写。 Here's an archive of a mailing list conversation I had on the qt-interest mailing list on the topic: 这是我在qt-interest邮件列表上关于该主题的邮件列表对话的存档:

http://blog.hostilefork.com/qt-model-view-different-threads/ http://blog.hostilefork.com/qt-model-view-different-threads/

"The short version is that I don't think it's feasible for a Model to “简短的版本是我认为模型不可行
be modified on a non-GUI thread...regardless of whether the model's 在非GUI线程上进行修改......无论模型是否正确
data has been protected with read/write locks. 数据已通过读/写锁保护。 If what I'm gathering 如果我正在收集
is correct, then Qt should probably have an assert that a model and 是正确的,然后Qt应该有一个断言模型和
its view have the same thread affinity (it doesn't seem to do that now)" 它的视图具有相同的线程亲和力(现在似乎没有这样做)“

A subsequent unit test by a KDE developer verified this. KDE开发人员进行的后续单元测试验证了这一点。

I feel the best way to work around this is to keep the model and the view on the same thread, and only modify the model in the GUI thread. 我觉得解决这个问题的最好方法是将模型和视图保持在同一个线程上,并且只修改GUI线程中的模型。 So if the worker thread wishes to change it then it should use a signal. 因此,如果工作者线程希望更改它,那么它应该使用信号。

Whether the worker needs to keep their own copy of the data from which the model was created (or if it needs to get notifications to keep that up to date when the user changes the model through the view) depends on your app. 工作者是否需要保留自己创建模型的数据副本(或者当用户通过视图更改模型时是否需要通知以使其保持最新)取决于您的应用程序。 If I understand you correctly, it sounds like like you could probably get away with just ferrying the updates through signal/slots and forgetting them on the worker... 如果我理解正确的话,听起来好像你可能只是通过信号/插槽传送更新而忘记工作人员......

I learned another potential problem today, the hard way, even if the model was read only. 即使模型是只读的,我今天也学到了另一个潜在的问题。 I use another thread to modify the data in the model (actually, my program is over 20 threads, and they all play nice), which then a Qt timer updates. 我使用另一个线程来修改模型中的数据(实际上,我的程序超过20个线程,它们都很好),然后Qt计时器更新。 This works very well, but there is a problem I fell into, which is: 这很有效,但我遇到了一个问题,那就是:

You cannot lock between rowCount / columnCount and data() . 您无法在rowCount / columnCountdata()之间进行锁定。

Qt works sequentially, meaning that in human language, it'll ask "how big are you", and then ask "what data do you have at this position", and these are prone to break. Qt按顺序工作,这意味着在人类语言中,它会问“你有多大”,然后问“你在这个位置有什么数据”,这些很容易破解。

Consider: 考虑:

int FilesQueue::rowCount(const QModelIndex &/*parent*/) const
{
    std::lock_guard<decltype(mainQueueMutex)> lg(mainQueueMutex);
    return filesQueue.size();
}
QVariant FilesQueue::data(const QModelIndex &index, int role) const
{
    std::lock_guard<decltype(mainQueueMutex)> lg(mainQueueMutex);
    if ( role == Qt::DisplayRole) {
        return filesQueue[index.row()]->getFilename();
    }
}

Qt will do the calls like this: Qt会做这样的调用:

//...
obj->rowCount();
obj->data(...);
//...

And I had assertion failure all over the place because simply, between rowCount() and data() , there was a thread that was changing the size of the data! 而且我在整个地方都有断言失败,因为简单地说,在rowCount()data() ,有一个线程正在改变数据的大小! It broke the program. 它打破了这个计划。 So this was happening: 所以这发生了:

//...
obj->rowCount();
//another thread: filesQueue.erase(...)
obj->data(...);
//...

My solution to the problem is to verify the size, again, in the data() method: 我对此问题的解决方案是再次在data()方法中验证大小:

QVariant FilesQueue::data(const QModelIndex &index, int role) const
{
    std::lock_guard<decltype(mainQueueMutex)> lg(mainQueueMutex);
    //solution is here:
    if(static_cast<int>(filesQueue.size()) <= index.row())
        return QVariant();
    if ( role == Qt::DisplayRole) {
        return filesQueue[index.row()]->getFilename();
    }
}

and there goes 3 hours of my life I'll never get back :-) 我生命中有3个小时我永远不会回来:-)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM