2017-01-28 26 views
6

Các Arc<T> documentation nói:Tại sao `Arc <T>` yêu cầu T là cả hai `Gửi` và` Đồng bộ hóa 'để có thể là `Gửi` /` Đồng bộ `?

impl<T> Sync for Arc<T> where T: Send + Sync + ?Sized 
impl<T> Send for Arc<T> where T: Send + Sync + ?Sized 

Một Arc cho phép nhiều luồng đồng thời truy cập vào T cơ bản thông qua một tài liệu tham khảo không thay đổi &T. Điều này là an toàn miễn là T không thể sửa đổi theo cách không đồng bộ qua &T. Điều này đúng với tất cả các loại có 'khả năng đột biến được thừa kế' (gần như tất cả các loại) và sai cho các loại không đồng bộ 'khả năng đột biến nội bộ' (ví dụ: RefCell, ...).

Theo như tôi hiểu, thì ràng buộc Send không bắt buộc tại đây. Ví dụ: Tôi nghĩ rằng chia sẻ my artificial type which implements Sync but not Send trong một Arc là an toàn.

Cuối cùng, &T chính nó cũng không bị ràng buộc này! Trong tài liệu cho SendSync chúng ta thấy:

impl<'a, T> Send for &'a T where T: Sync + ?Sized 
impl<'a, T> Sync for &'a T where T: Sync + ?Sized 

Và như Arc<T> cho phép người truy cập cùng với T như &T không, tôi không hiểu tại sao Arc<T> có thêm Send ràng buộc. Tại sao lại như vậy?

+0

Tôi tự hỏi đây có phải là dấu tích còn lại khi 'Gửi' ngụ ý' tĩnh', vì việc chia sẻ dữ liệu qua các chuỗi yêu cầu 'tĩnh' trong thư viện chuẩn tại thời điểm này. –

Trả lời

8

Tôi tin này là bởi vì một Arcsở hữu giá trị nó chứa, và do đó chịu trách nhiệm về thả nó.

xem xét theo trình tự sau:

  • Một giá trị của loại T được tạo ra trong chủ đề 1. Nó không phải là Send, có nghĩa là nó là không an toàn để di chuyển giá trị này để thread khác.
  • Giá trị này được chuyển vào một ô điều khiển Arc.
  • Một bản sao của tay cầm được gửi đến chủ đề 2.
  • Tay cầm được lưu trữ theo chuỗi 1 bị loại bỏ.
  • Tay cầm được lưu trữ theo chuỗi 2 bị loại bỏ. Vì đây là lần xử lý cuối cùng, nó giả định quyền sở hữu đầy đủ của giá trị được lưu trữ và loại bỏ nó.

Và cứ như thế, chúng tôi đã chuyển giá trị loại T từ chuỗi này sang chủ đề khác, vi phạm an toàn bộ nhớ.

&T không đòi hỏi Send vì thả một &Tbao giờ cho phép bạn thả các giá trị cơ bản.

Hợp đồng bổ sung: Ví dụ về loại mà vấn đề này sẽ là vấn đề, hãy xem xét loại như struct Handle(usize); được hỗ trợ bởi một loạt tài nguyên chuỗi địa phương.Nếu triển khai thực hiện Drop cho loại như vậy chạy sai chuỗi, điều này sẽ dẫn đến việc hoặc thực hiện truy cập ngoài giới hạn (nơi cố gắng hủy tài nguyên không tồn tại trên chuỗi này) hoặc phá hủy một tài nguyên vẫn đang được sử dụng.

+0

Ahh! Phải, cảm ơn bạn! @Matthieu M. thậm chí còn đề cập đến truy cập ẩn thông qua 'drop()' trong câu hỏi khác. Vì vậy, tôi đoán tuyên bố của tôi "' Arc 'chỉ truy cập' T' trong cùng một cách '& T' không" là sai, vì 'Arc ' truy cập dễ dàng 'T' cho' drop() '. Tuyệt vời! –

+0

Một điều suy nghĩ: bạn có thể sử dụng loại 'Foo' của tôi từ câu hỏi khác để cho thấy rằng chúng ta * có thể * đạt được an toàn không? Tôi nghĩ một ví dụ cụ thể dễ hiểu hơn "một' T' không phải là 'Gửi'". –

+2

@LukasKalbertodt Tôi không nghĩ rằng ví dụ cụ thể sẽ là một vấn đề * trong thực tế. * Off đầu của tôi, những thứ như xử lý vào lưu trữ thread-local, hoặc giá trị hệ điều hành chỉ được sử dụng từ một chủ đề cụ thể đến lí trí. Hãy suy nghĩ một cái gì đó giống như 'struct Handle (usize);' chỉ mục đó thành một mảng tài nguyên chuỗi địa phương; thả nó vào chuỗi sai sẽ dẫn đến truy cập ngoài giới hạn hoặc xóa tài nguyên sai. –

Các vấn đề liên quan