std :: shared_ptr thread an toàn được giải thích


106

Tôi đang đọc http://gcc.gnu.org/onlineocs/libstdc++/manual/shared_ptr.html và tôi vẫn chưa rõ một số vấn đề về an toàn chuỗi:

  1. Tiêu chuẩn đảm bảo rằng việc đếm tham chiếu được xử lý an toàn cho chuỗi và nó độc lập với nền tảng, phải không?
  2. Vấn đề tương tự - tiêu chuẩn đảm bảo rằng chỉ một luồng (giữ tham chiếu cuối cùng) sẽ gọi xóa trên đối tượng được chia sẻ, phải không?
  3. shared_ptr không đảm bảo an toàn luồng nào cho đối tượng được lưu trữ trong đó?

BIÊN TẬP:

Mã giả:

// Thread I
shared_ptr<A> a (new A (1));

// Thread II
shared_ptr<A> b (a);

// Thread III
shared_ptr<A> c (a);

// Thread IV
shared_ptr<A> d (a);

d.reset (new A (10));

Gọi reset () trong luồng IV sẽ xóa cá thể trước đó của lớp A được tạo trong luồng đầu tiên và thay thế nó bằng cá thể mới? Hơn nữa sau khi gọi reset () trong luồng IV các luồng khác sẽ chỉ thấy đối tượng mới được tạo?


24
Đúng, đúng, và đúng.
spff

16
bạn nên sử dụng make_sharedthay vìnew
qdii

Câu trả lời:


87

Như những người khác đã chỉ ra, bạn đã hiểu đúng về 3 câu hỏi ban đầu của mình.

Nhưng phần cuối của bản chỉnh sửa của bạn

Gọi reset () trong luồng IV sẽ xóa cá thể trước đó của lớp A được tạo trong luồng đầu tiên và thay thế nó bằng cá thể mới? Hơn nữa sau khi gọi reset () trong luồng IV các luồng khác sẽ chỉ thấy đối tượng mới được tạo?

là không chính xác. Chỉ dsẽ trỏ đến mới A(10), và a, bcsẽ tiếp tục đến thời điểm với bản gốc A(1). Có thể thấy rõ điều này trong ví dụ ngắn sau đây.

#include <memory>
#include <iostream>
using namespace std;

struct A
{
  int a;
  A(int a) : a(a) {}
};

int main(int argc, char **argv)
{
  shared_ptr<A> a(new A(1));
  shared_ptr<A> b(a), c(a), d(a);

  cout << "a: " << a->a << "\tb: " << b->a
     << "\tc: " << c->a << "\td: " << d->a << endl;

  d.reset(new A(10));

  cout << "a: " << a->a << "\tb: " << b->a
     << "\tc: " << c->a << "\td: " << d->a << endl;
                                                                                                                 
  return 0;                                                                                                          
}

(Rõ ràng, tôi không bận tâm đến bất kỳ luồng nào: điều đó không ảnh hưởng đến shared_ptr::reset()hành vi.)

Đầu ra của mã này là

a: 1 b: 1 c: 1 d: 1

a: 1 b: 1 c: 1 d: 10


35
  1. Đúng, shared_ptrs sử dụng tăng / giảm nguyên tử của một giá trị đếm tham chiếu.

  2. Tiêu chuẩn đảm bảo chỉ một luồng sẽ gọi toán tử xóa trên một đối tượng được chia sẻ. Tôi không chắc liệu nó có chỉ định cụ thể luồng cuối cùng xóa bản sao của con trỏ được chia sẻ hay không sẽ là luồng gọi xóa (có thể trong thực tế sẽ là trường hợp này).

  3. Không, không, đối tượng được lưu trữ trong đó có thể được chỉnh sửa đồng thời bởi nhiều luồng.

CHỈNH SỬA: Theo dõi nhẹ, nếu bạn muốn có ý tưởng về cách con trỏ được chia sẻ hoạt động nói chung, bạn có thể muốn xem boost::shared_ptrnguồn: http://www.boost.org/doc/libs/1_37_0/boost/shared_ptr.hpp .


3
1. Khi bạn nói "" shared_ptrs ', hãy sử dụng số gia / giảm nguyên tử của một giá trị số tham chiếu. " Có phải ý bạn là họ không sử dụng bất kỳ khóa nội bộ nào để tăng / giảm nguyên tử, mà ngữ cảnh sẽ chuyển đổi? Trong ngôn ngữ đơn giản, nhiều luồng có thể tăng / giảm số lượng tham chiếu mà không cần sử dụng khóa? Việc tăng nguyên tử được thực hiện bởi các hướng dẫn đặc biệt atom_test_and_swap / atom_test_and_increment?
rahul.deshmukhpatil

@rahul trình biên dịch miễn phí sử dụng mutex / lock, nhưng hầu hết các trình biên dịch tốt sẽ không sử dụng mutex / lock trên các nền tảng mà nó có thể được thực hiện miễn phí.
Bernard

@Bernard: ý bạn là nó phụ thuộc vào việc triển khai "trình biên dịch std lib shared_ptr" cho nền tảng?
rahul.deshmukhpatil 27/02/17

2
Đúng. Theo sự hiểu biết của tôi, tiêu chuẩn không nói rằng nó phải không có khóa. Nhưng trong GCC và MSVC mới nhất, nó không bị khóa trên phần cứng Intel x86 và tôi nghĩ rằng các trình biên dịch tốt khác có khả năng làm như vậy khi phần cứng hỗ trợ nó.
Bernard

18

std::shared_ptr không phải là chủ đề an toàn.

Con trỏ dùng chung là một cặp gồm hai con trỏ, một con trỏ tới đối tượng và một con trỏ tới khối điều khiển (giữ bộ đếm tham chiếu, liên kết đến con trỏ yếu ...).

Có thể có nhiều std :: shared_ptr và bất cứ khi nào chúng truy cập vào khối điều khiển để thay đổi bộ đếm tham chiếu, nó an toàn theo luồng nhưng std::shared_ptrbản thân nó KHÔNG an toàn luồng hoặc nguyên tử.

Nếu bạn gán một đối tượng mới std::shared_ptrtrong một thời gian luồng khác sử dụng nó, nó có thể kết thúc với con trỏ đối tượng mới nhưng vẫn sử dụng một con trỏ đến khối điều khiển của đối tượng cũ => CRASH.


4
Chúng tôi có thể nói rằng std::shared_ptrtrường hợp đơn lẻ không an toàn cho chuỗi. Từ tham khảo std :: shared_ptr:If multiple threads of execution access the same shared_ptr without synchronization and any of those accesses uses a non-const member function of shared_ptr then a data race will occur;
JKovalsky

Điều này có thể được nói tốt hơn. Một std::shared_ptr<T>phiên bản được đảm bảo an toàn cho luồng khi luôn được sử dụng bởi giá trị (được sao chép / di chuyển) qua các ranh giới luồng. Tất cả các mục đích sử dụng khác, std::shared_ptr<T>&đều không an toàn qua các ranh giới chủ đề
WhiZTiM
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.