2012-02-28 61 views
11

我正在创建一个应用程序,它显示市场数据并在其他一些形式中使用它。我在地图中存储市场数据,说 std::map<tickerId, StockData>。让我举一个这个地图可以如何使用的例子。设计模式,Qt模型/视图和多线程

  1. 网络在时间t发送封装股票数据的数据包。 updatePrice(tickerId, latestPrice)
  2. 更新地图中的股票数据。现在,多个线程可以访问/更新数据。所以为了线程安全的操作,地图必须被锁定。这是第一个问题,我是否需要锁定基础数据以获取更新?
  3. 新股票数据有多种用途,例如IBM有价格更新,那么我需要更新我的投资组合中IBM的价值。以及在屏幕上显示新数据。并且可以有其他几个同时使用。 updatePosition(tickerId, price)updateStockScreen(tickerId, price)。此外,从位置更新中分离Gui更新很重要,因为GUI不是应用程序的主要优点。
  4. 我只是对如何实现这种类型的设计感到困扰。我阅读了QT中的Model/View Design来显示数据,但如果View线程从相同的地图读取,它必须被锁定。这导致设计缓慢/低效。每次从模型读取视图时,都需要锁定模型。这是在实时GUI中提供的吗?
  5. 总之,我已经将很多不同的对象存储为地图。对象实时更新。我需要更新它们,然后在各个位置使用它们。如果有人能够给我一个关于如何实现这种设计的小例子,那将是非常好的。

有用的书的一些参考也赞赏。

我是新的,并试图用我的小知识实现太多,所以如果我问过愚蠢/格式不清的问题,请原谅我。

感谢 希夫

+0

我刚刚在这里回答了一个类似的问题到这一个:http://stackoverflow.com/questions/9476045/can-two-threads-read-from-the-same-qlist-at-同时/ 9476153#9476153,虽然你的问题写得好得多,所以谢谢你!同意@HostileFork,我觉得使用信号是沟通数据的最佳方式。我想知道,你可以在一个线程中运行什么样的视图?这是一种非贵族观点吗? – jdi 2012-02-28 21:04:27

回答

9

像你想在一个线程模型,并在另一个角度,我看着在一个点听起来概念。

如果是这样......并且您的模型只读通过视图小部件,那么是的,你必须锁定。我认为这样做会破坏模型/视图分离提供的“解耦”的优雅性。但它可以工作。

但是,如果您的模型是通过视图读写的,则由于通知插槽的排队性质,无法正确执行,因为所有都是如此。这里有一个邮件列表的谈话,我有QT-interest邮件列表上的话题档案:

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

“简短的版本是,我不认为这是可行的模型,以
在非GUI线程上被修改......无论该模型的
数据是否被读/写锁保护。如果有什么我收集
是正确的,那么Qt的应该可能有一个断言模型和
其观点具有相同的线程关联性(它似乎并没有这样做,现在)”

一后续由KDE开发人员进行的单元测试验证了这一点

我觉得解决这个问题的最好方法是保持模型和视图在同一个线程上,并且只修改GUI线程中的模型。线程希望改变它然后它应该使用一个信号

是否工人n让他们保留自己创建模型的数据副本(或者如果需要获取通知以便在用户通过视图更改模型时保持最新状态)取决于您的应用程序。如果我理解正确的话,听起来好像你很可能逃脱刚刚运送通过信号/槽更新,忘记他们的工人......

+0

+1用于建议通过信号进行通信 – jdi 2012-02-28 21:07:53

+0

那么,模型是由一个单独的线程完全更新,我无法控制它。我想我需要想出一些管道设计模型来实现我所需要的。 – 2012-02-28 21:30:50

+1

@ShivChawla孤男寡女来从一个单独的线程来的事件做出反应并不意味着你也必须在该线程实例化'QAbstractItemModel'派生类。模型应该只能在GUI线程上运行。这是一个有点棘手的,但请给一个彻底的读/上面的链接重新阅读理解为什么是这样的话... – HostileFork 2012-02-28 21:58:40

0

我今天学到了潜在的问题,困难的方式,即使该模型是只读的。我使用另一个线程修改模型中的数据(实际上,我的程序超过20个线程,并且它们都很好),然后Qt计时器更新。这个作品非常好,但有一个问题,我陷入了,那就是:

不能rowCount/columnCountdata()之间的锁定。

Qt按顺序工作,这意味着在人类语言中,它会问“你有多大”,然后问“你在这个位置有什么数据”,而且这些数据很容易中断。

考虑:

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会做这样的呼吁:

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

我曾断言失败所有的地方,因为简单,rowCount()data()之间,有一个线程正在改变数据的大小!它打破了这个计划。因此,这是怎么回事:

//... 
obj->rowCount(); 
//another thread: filesQueue.erase(...) 
obj->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(); 
    } 
} 

有去3小时我的生活我会的永远不会回来:-)