簡體   English   中英

如何安全地從多個線程並行訪問和寫入復雜容器?

[英]How to safely access and write to a complex container from multiple threads in parallel?

我有一個結構的 unordered_map 的情況。 該結構包含 int(s)、bool(s) 和一個向量。 My program will fetch data for each item in the map either through a https call to a server or using websocket (seperate https calls are required for each item in map). 使用 websocket 時,map 中的所有項目的數據一起返回。 提取的數據被處理並存儲在相應的向量中。

websocket 在單獨的線程中運行,應該在程序的整個生命周期中運行。

我的程序有一個刪除 function 可以“清空”整個 map。 還有一個 addItem() function,它將向我的 map 添加新結構。

只要結構的“updatesOn”成員為假,就不會將數據推入向量中。

我當前的實現有 3 個線程:

  • 主線程將向 map 添加新項目。 另一個主線程的function是從struct中的vector取數據。 主線程有一個 function 清空 map 並重新開始。 它還有另一個 function 只清空向量。
  • 第二個線程將運行 websocket 客戶端並在新數據到達時填充結構中的向量。 有一個 while 循環檢查退出標志。 一旦在主線程中設置了退出標志,該線程就會終止。
  • 第三個線程是管理器線程。 它會在 map 中查找新條目,並下載 http,然后將此條目添加到 websocket 以進行后續數據更新。 它還定期運行 http 下載,清空向量並重新填充它。

現在我有兩個互斥鎖。

  • 一種用於在向向量寫入/讀取數據之前鎖定。
  • 第二個互斥鎖是在 map 中添加或刪除新數據時。 也可在 map 清空時使用。

我覺得這是互斥鎖的錯誤用法。 因為我可能會在讀取或寫入其結構的向量元素之一時清空 map。 這讓我為所有人使用一個互斥鎖。

問題是這是一個實時股票數據程序,即每秒都有新數據彈出,有時甚至更快。 恐怕所有人的一個互斥鎖可能會減慢我的整個應用程序的速度。

如上所述,所有 3 個線程都對這個 map 具有寫訪問權限,主線程能夠完全清空它。

牢記速度和線程安全,什么是實現這一點的好方法?

我的數據成員:

unordered_map<string, tickerDiary> tDiaries;

struct tickerDiary {
            tickerDiary() : name(""), ohlcPeriodicity("minute"), ohlcStatus(false), updatesOn(true), ohlcDayBarIndex(0), rtStatus(false) {}
            string name; 
            string ohlcPeriodicity; 
            bool ohlcStatus; 
            bool rtStatus;
            bool updatesOn;
            int32 ohlcDayBarIndex;
            vector<Quotation> data;
};

struct Quotation {
            union AmiDate DateTime;
            float   Price;
            float   Open;
            float   High;
            float   Low;
            float   Volume;
            float   OpenInterest;
            float   AuxData1;
            float   AuxData2;
};

注意:我使用的是 C++11。

如果我正確理解您的問題,您的 map 本身主要編寫在主線程中,其他線程僅用於操作 map 中條目中包含的數據。

鑒於此,對於非主線程有兩個問題:

  1. 他們工作的項目不應該隨機消失
  2. 他們應該是唯一一個在他們的項目上工作的人。

第一個問題可以通過將存儲與 map 分離來最有效地解決。 因此,對於每個項目,存儲都是單獨分配的(通過默認分配器,或者如果您添加/刪除大量項目,則通過一些池方案),並且 map 僅存儲一個共享 ptr。 然后每個處理一個項目的線程只需要保持一個共享的ptr,以確保存儲不會從它們下面消失。 然后僅在指針的獲取/存儲/刪除期間才需要獲取映射的關聯互斥鎖/共享互斥鎖。 只要可以接受某些線程可能會浪費一些時間對已從 map 中刪除的項目執行操作,這將正常工作。 使用 shared_ptrs 將確保您不會通過使用引用計數器來泄漏 memory,並且它們還將對這些引用計數進行鎖定/解鎖(或者更確切地說,嘗試對這些引用使用更有效的平台原語)。 如果您想了解更多關於 shared_ptr 和一般智能指針的信息,是對 c++ 智能指針系統的合理介紹。

這留下了第二個問題,這可能最容易通過在數據結構(tickerDiary)本身中保留一個互斥鎖來解決,線程在開始執行需要從結構中預測行為的操作時獲取,並且可以在他們完成后釋放他們應該這樣做。

以這種方式分離鎖定應該可以減少 map 的全局鎖定的爭用量。 但是,考慮到各個項目的分配和引用計數的額外成本,您可能應該對您的代碼進行基准測試,看看這種減少是否值得。

我不認為在這里使用std::vector是正確的集合。 但是如果你堅持使用它,你應該為每個集合設置一個互斥鎖。

我會推薦來自INTEL TBBconcurrent_vector或來自boost的同步數據結構。

第三種解決方案可能是實現您自己的並發向量

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM