std :: shared_ptr เธรดปลอดภัยอธิบาย


106

ฉันกำลังอ่านhttp://gcc.gnu.org/onlinedocs/libstdc++/manual/shared_ptr.htmlและปัญหาด้านความปลอดภัยของเธรดยังไม่ชัดเจนสำหรับฉัน:

  1. มาตรฐานรับประกันว่าการนับอ้างอิงได้รับการจัดการเธรดอย่างปลอดภัยและเป็นอิสระจากแพลตฟอร์มใช่ไหม
  2. ปัญหาที่คล้ายกัน - มาตรฐานรับประกันว่าเธรดเดียวเท่านั้น (ถือการอ้างอิงสุดท้าย) จะเรียกลบในวัตถุที่แชร์ใช่ไหม
  3. shared_ptr ไม่รับประกันความปลอดภัยของเธรดใด ๆ สำหรับอ็อบเจ็กต์ที่เก็บอยู่ในนั้น?

แก้ไข:

รหัสหลอก:

// 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));

การเรียกการรีเซ็ต () ในเธรด IV จะลบอินสแตนซ์ก่อนหน้าของคลาส A ที่สร้างในเธรดแรกและแทนที่ด้วยอินสแตนซ์ใหม่? ยิ่งไปกว่านั้นหลังจากเรียกการรีเซ็ต () ในเธรด IV เธรดอื่น ๆ จะเห็นเฉพาะวัตถุที่สร้างขึ้นใหม่?


24
ขวาขวาและขวา
spraff

16
คุณควรใช้make_sharedแทนnew
qdii

คำตอบ:


87

ตามที่คนอื่น ๆ ชี้ให้เห็นคุณได้คิดอย่างถูกต้องเกี่ยวกับคำถาม 3 ข้อเดิมของคุณ

แต่ส่วนท้ายของการแก้ไขของคุณ

การเรียกการรีเซ็ต () ในเธรด IV จะลบอินสแตนซ์ก่อนหน้าของคลาส A ที่สร้างในเธรดแรกและแทนที่ด้วยอินสแตนซ์ใหม่? ยิ่งไปกว่านั้นหลังจากเรียกการรีเซ็ต () ในเธรด IV เธรดอื่น ๆ จะเห็นเฉพาะวัตถุที่สร้างขึ้นใหม่?

ไม่ถูกต้อง เพียง แต่dจะชี้ไปที่ใหม่A(10)และa, bและจะยังคงชี้ไปที่เดิมc A(1)สิ่งนี้สามารถเห็นได้อย่างชัดเจนในตัวอย่างสั้น ๆ ต่อไปนี้

#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;                                                                                                          
}

(เห็นได้ชัดว่าฉันไม่ได้กังวลกับเธรดใด ๆ : นั่นไม่ได้คำนึงถึงshared_ptr::reset()พฤติกรรม)

ผลลัพธ์ของรหัสนี้คือ

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

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


35
  1. ถูกต้องshared_ptrใช้การเพิ่ม / ลดอะตอมของค่าการนับอ้างอิง

  2. มาตรฐานรับประกันเธรดเดียวเท่านั้นที่จะเรียกใช้ตัวดำเนินการลบบนอ็อบเจ็กต์ที่แชร์ ฉันไม่แน่ใจว่ามันระบุเฉพาะเธรดสุดท้ายที่ลบสำเนาของตัวชี้ที่ใช้ร่วมกันหรือไม่จะเป็นตัวที่เรียกลบ (ในทางปฏิบัติจะเป็นเช่นนั้น)

  3. ไม่เลยวัตถุที่เก็บไว้ในนั้นสามารถแก้ไขได้พร้อมกันโดยใช้หลายเธรด

แก้ไข: ติดตามเล็กน้อยถ้าคุณต้องการที่จะได้รับความคิดของวิธีชี้ที่ใช้ร่วมกันทำงานในทั่วไปคุณอาจต้องการที่จะมองไปที่boost::shared_ptrมา: http://www.boost.org/doc/libs/1_37_0/boost/shared_ptr.hpp


3
1. เมื่อคุณพูดว่า "'shared_ptrs' ให้ใช้การเพิ่มขึ้น / ลดลงของค่าการนับอ้างอิง" คุณหมายความว่าพวกเขาไม่ได้ใช้การล็อกภายในใด ๆ สำหรับการเพิ่ม / ลดของอะตอมซึ่งบริบทจะเปลี่ยนไปหรือไม่? ในภาษาง่ายๆเธรดหลายเธรดสามารถเพิ่ม / ลดจำนวนการอ้างอิงได้โดยไม่ต้องใช้การล็อกหรือไม่? การเพิ่มขึ้นของอะตอมทำได้โดยคำสั่ง atomic_test_and_swap / atomic_test_and_increment พิเศษ?
rahul.deshmukhpatil

@rahul คอมไพเลอร์มีอิสระที่จะใช้ mutex / lock แต่คอมไพเลอร์ที่ดีส่วนใหญ่จะไม่ใช้ mutex / lock บนแพลตฟอร์มที่สามารถทำได้โดยไม่ต้องล็อค
เบอร์นาร์ด

@ เบอร์นาร์ด: คุณหมายความว่ามันขึ้นอยู่กับการใช้งาน "คอมไพเลอร์ std lib shared_ptr" สำหรับแพลตฟอร์มหรือไม่?
rahul.deshmukhpatil

2
ใช่. จากความเข้าใจของฉันมาตรฐานไม่ได้บอกว่าต้องล็อคฟรี แต่ใน GCC และ MSVC ล่าสุดไม่มีการล็อกบนฮาร์ดแวร์ Intel x86 และฉันคิดว่าคอมไพเลอร์ที่ดีอื่น ๆ ก็น่าจะทำเช่นเดียวกันเมื่อฮาร์ดแวร์รองรับ
Bernard

18

std::shared_ptr เธรดไม่ปลอดภัย

ตัวชี้ที่ใช้ร่วมกันคือคู่ของสองพอยน์เตอร์ตัวหนึ่งไปยังอ็อบเจ็กต์และอีกตัวหนึ่งไปยังบล็อกควบคุม (ถือตัวนับอ้างอิงลิงก์ไปยังตัวชี้ที่อ่อนแอ ... )

สามารถมีหลาย std :: shared_ptr และเมื่อใดก็ตามที่พวกเขาเข้าถึงบล็อกควบคุมเพื่อเปลี่ยนตัวนับการอ้างอิงมันปลอดภัยสำหรับเธรด แต่std::shared_ptrตัวเองไม่ปลอดภัยเธรดหรือปรมาณู

หากคุณกำหนดอ็อบเจ็กต์ใหม่std::shared_ptrในขณะที่เธรดอื่นใช้อ็อบเจ็กต์นั้นอาจลงเอยด้วยตัวชี้อ็อบเจ็กต์ใหม่ แต่ยังคงใช้ตัวชี้ไปยังบล็อกควบคุมของอ็อบเจ็กต์เก่า => CRASH


4
เราสามารถพูดได้ว่าstd::shared_ptrอินสแตนซ์เดียวไม่ปลอดภัยต่อเธรด จาก 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

สิ่งนี้สามารถพูดได้ดีกว่า std::shared_ptr<T>รับประกันว่าอินสแตนซ์เธรดปลอดภัยเมื่อใช้โดยค่า (คัดลอก / ย้าย) ข้ามขอบเขตเธรดเสมอ การใช้งานอื่น ๆ ทั้งหมดstd::shared_ptr<T>&ไม่ปลอดภัยในขอบเขตของเธรด
WhiZTiM
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.