https://en.cppreference.com/w/cpp/memory/shared_ptr/use_count狀態:
在多執行緒環境中,use_count 回傳的值是近似值(典型實作使用 memory_order_relaxed 加載)
但這是否意味著use_count()在多執行緒環境中完全沒用?
考慮以下示例,其中Circular類實作了std::shared_ptr<int>.
向用戶提供了一種方法 - get(),它檢查next元素的參考計數是否std::array<std::shared_ptr<int>>大于 1(我們不希望這樣做,因為這意味著它被先前呼叫的用戶持有get())。
如果是<= 1,則將 的副本std::shared_ptr<int>回傳給用戶。
在這種情況下,用戶是兩個執行緒,除了喜歡呼叫回圈緩沖區之外什么都不做get()——這就是他們的生活目的。
當我執行程式時實際發生的情況是它運行了幾個周期(通過將 a 添加counter到回圈緩沖區類進行測驗),之后它拋出例外,抱怨下一個元素的參考計數器是> 1.
這use_count()是在多執行緒環境中回傳的值是近似值的陳述句的結果嗎?
是否可以調整底層機制以使其具有確定性并按照我希望的方式運行?
如果我的想法是正確的 - use_count()(或者更確切地說是真實的用戶數量)在函式next內部時元素的值永遠不會超過 1 ,因為只有兩個消費者,并且每次執行緒呼叫時,它已經釋放了它的舊 (復制)(這反過來意味著剩余的駐留應該只有 1 的參考計數)。get()Circularget()std::shared_ptr<int>std::shared_ptr<int>Circular::ints_
#include <mutex>
#include <array>
#include <memory>
#include <exception>
#include <thread>
class Circular {
public:
Circular() {
for (auto& i : ints_) { i = std::make_shared<int>(0); }
}
std::shared_ptr<int> get() {
std::lock_guard<std::mutex> lock_guard(guard_);
index_ = index_ % 2; // Re-set the index pointer.
if (ints_.at(index_).use_count() > 1) {
// This shouldn't happen - right? (but it does)
std::string excp = std::string("OOPSIE: ") std::to_string(index_) " " std::to_string(ints_.at(index_).use_count());
throw std::logic_error(excp);
}
return ints_.at(index_ );
}
private:
std::mutex guard_;
unsigned int index_{0};
std::array<std::shared_ptr<int>, 2> ints_;
};
Circular circ;
void func() {
do {
auto scoped_shared_int_pointer{circ.get()};
}while(1);
}
int main() {
std::thread t1(func), t2(func);
t1.join(); t2.join();
}
uj5u.com熱心網友回復:
雖然use_count充滿了問題,但現在的核心問題超出了這個邏輯。
假設執行緒t1獲取shared_ptrat 索引 0,然后在完成第一次回圈迭代 t2之前運行它的回圈兩次。將獲得at 索引 1,釋放它,然后嘗試獲取at 索引 0,并且會遇到您的失敗條件,因為它只是在后面運行。t1t2shared_ptrshared_ptrt1
現在,也就是說,在更廣泛的背景下,它并不是特別安全,就像用戶創建 a 一樣weak_ptr,完全有可能在use_count不通過此函式的情況下從 1 變為 2。在這個簡單的例子中,它可以回圈遍歷索引陣列,直到找到空閑的共享指標。
uj5u.com熱心網友回復:
use_count僅用于除錯,不應使用。如果您想知道其他人何時不再參考指標,只需讓共享指標死亡并使用自定義洗掉器來檢測它并對現在未使用的指標執行您需要做的任何事情。
這是您如何在代碼中實作此功能的示例:
#include <mutex>
#include <array>
#include <memory>
#include <exception>
#include <thread>
#include <vector>
#include <iostream>
class Circular {
public:
Circular() {
size_t index = 0;
for (auto& i : ints_)
{
i = 0;
unused_.push_back(index );
}
}
std::shared_ptr<int> get() {
std::lock_guard<std::mutex> lock_guard(guard_);
if (unused_.empty())
{
throw std::logic_error("OOPSIE: none left");
}
size_t index = unused_.back();
unused_.pop_back();
return std::shared_ptr<int>(&ints_[index], [this, index](int*) {
std::lock_guard<std::mutex> lock_guard(guard_);
unused_.push_back(index);
});
}
private:
std::mutex guard_;
std::vector<size_t> unused_;
std::array<int, 2> ints_;
};
Circular circ;
void func() {
do {
auto scoped_shared_int_pointer{ circ.get() };
} while (1);
}
int main() {
std::thread t1(func), t2(func);
t1.join(); t2.join();
}
保留未使用索引串列,當共享指標被銷毀時,自定義洗掉器將索引回傳到未使用索引串列,準備在下一次呼叫時使用get。
轉載請註明出處,本文鏈接:https://www.uj5u.com/qiye/451621.html
