2012-02-28 68 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小時我的生活我會的永遠不會回來:-)