2009-06-03 31 views
8

Mọi người đều biết rằng đây không phải là chủ đề an toàn:Là C# '??' thread điều hành an toàn?

public StringBuilder Builder 
{ 
    get 
    { 
     if (_builder != null) 
      _builder = new StringBuilder(); 
     return _builder; 
    } 
} 

Điều này thì sao?

public StringBuilder Builder 
{ 
    get { return _builder ?? (_builder = new StringBuilder()); } 
} 
+13

Đặc tả C# cẩn thận gọi ra những hoạt động nào là nguyên tử; null toán tử kết hợp không phải là nguyên tử. Toán tử kết hợp không chỉ là một cú pháp cú pháp cho đoạn mã đầu tiên của bạn. Nhưng bạn có vấn đề lớn hơn ở đây; ai quan tâm nếu trường là an toàn? Người xây dựng không an toàn! –

+4

Đối với câu hỏi trong tương lai trong tĩnh mạch này, nó sẽ giúp nếu bạn cung cấp một định nghĩa cẩn thận từ chính xác những gì "thread an toàn" có nghĩa là cho bạn. An toàn chủ đề không phải là tuyệt đối; thay vào đó, mã là thread an toàn nếu hợp đồng sử dụng được thực hiện bởi người gọi tương thích với dự kiến ​​của callee. Không biết bạn đang mong đợi gì, bạn không thể nói liệu mã có tuân theo hay không. –

Trả lời

10

BEGIN EDIT

Dựa trên tiêu đề thay đổi nội dung của bạn, các nhà điều hành null-coalescing bản thân có vẻ là thread-safe (xem Phil Haack's analysis). Nó xuất hiện, tuy nhiên, nó không đảm bảo chống lại các cuộc gọi nhiều tiềm năng đến các nhà xây dựng StringBuilder.

END EDIT

Bạn có một vấn đề lớn hơn với luồng, và đó là những Builder tài sản riêng của mình đại diện cho nhà nước có thể được chia sẻ trên chủ đề. Ngay cả khi bạn làm cho thread khởi tạo lười biếng an toàn, không có gì đảm bảo rằng các phương thức tiêu thụ Builder đang thực hiện nó theo một cách an toàn.

// below code makes the getter thread safe 
private object builderConstructionSynch = new object(); 
public StringBuilder Builder 
{ 
    get 
    { 
     lock (builderConstructionSynch) 
     { 
      if (_builder == null) _builder = new StringBuilder(); 
     } 
     return _builder; 
    } 
} 

trên sẽ ngăn chặn vấn đề luồng trong việc khởi tạo lười biếng của _builder, nhưng trừ khi bạn đồng bộ hóa các cuộc gọi của bạn với các phương pháp thể hiện của StringBuilder, bạn đang không được bảo đảm an toàn thread trong bất kỳ phương pháp mà tiêu thụ tài sản Builder. Điều này là do các phương thức thể hiện trong StringBuilder không được thiết kế để làm chủ đề an toàn. Xem văn bản bên dưới từ MSDN StringBuilder page.

Bất kỳ thành phần tĩnh công cộng nào (được chia sẻ trong Visual Cơ bản) thuộc loại này là sợi an toàn. Bất kỳ thành viên cá thể nào không phải là được đảm bảo là chuỗi an toàn.

Nếu bạn đang sử dụng StringBuilder trong nhiều chủ đề, bạn có thể phục vụ tốt hơn đóng gói nó trong lớp học của bạn. Đặt Trình tạo riêng tư và hiển thị hành vi nào bạn cần theo phương thức công khai:

public void AppendString(string toAppend) 
{ 
    lock (Builder) 
    { 
     Builder.Append(toAppend); 
    } 
} 

Bằng cách này bạn không viết mã đồng bộ trên toàn bộ địa điểm.

+0

Tôi chỉ nghĩ rằng ?? là hoạt động nguyên tử. ? không phải là thread an toàn quá? –

+3

Tôi không thể nói liệu toán tử kết hợp null có phải là nguyên tử hay không, nhưng xác nhận của tôi là bạn có vấn đề lớn hơn vì StringBuilder không thực sự an toàn về luồng. –

+1

Xem câu trả lời đã chỉnh sửa để biết câu trả lời về an toàn luồng của nhà điều hành không hợp nhất (tín dụng cho Phil Haack). Đó là chủ đề an toàn inasmuch vì nó không tạo điều kiện chủng tộc, nhưng bạn có khả năng có thể kết thúc với hai trường hợp riêng biệt của Builder nếu điều kiện là hoàn hảo. –

8

NO cho cả hai phiên bản

10

Đó không phải là nhiều hay ít thread-safe; bạn vẫn có thể có hai chủ đề làm kiểm tra null cùng một lúc, do đó tạo ra các đối tượng riêng biệt và không nhìn thấy các đối tượng khác.

+0

Ý kiến ​​của bạn về việc sử dụng Interlocked.CompareExchange (ref _builder, StringBuilder mới(), null) là gì? – LBushkin

2

Không, không phải là nguyên tử

2

Câu trả lời được đưa ra là chính xác, cả hai đều không an toàn. Trong thực tế, chúng hầu hết là tương đương, và toán tử ?? chỉ là trình biên dịch ma thuật để làm cho mã gọn gàng hơn. Bạn cần phải sử dụng một số cơ chế đồng bộ hóa nếu bạn muốn điều này trở thành threadsafe.

2

tôi đã không kiểm tra này tiếp cận bản thân mình, nhưng nếu bạn muốn an toàn thread mà không có chi phí của một chương trình khóa và bạn không phải lo lắng về khả năng tạo ra và loại bỏ một trường hợp đối tượng, bạn có thể thử này:

using System.Threading; 

public StringBuilder Builder 
{ 
    get 
    { 
     if (_builder != null) 
      Interlocked.CompareExchange(ref _builder, new StringBuilder(), null); 
     return _builder; 
    } 
} 

Cuộc gọi đến CompareExchange() sẽ thực hiện thay thế nguyên tử giá trị trong _builder bằng phiên bản mới của StringBuilder chỉ khi _builder == null.Tất cả các phương thức trên lớp Interlocked đều được bảo đảm KHÔNG được preempted bởi các công tắc luồng.

+0

BTW, nó có thể là một ý tưởng tồi để chia sẻ một thể hiện của một chuỗi StringBuilder trên chủ đề. SB vốn không phải là chủ đề an toàn, và nó không rõ ràng, ngay cả khi nó được, rằng bạn có thể làm bất cứ điều gì có ý nghĩa với nó trên các chủ đề mà không được đồng bộ hóa. – LBushkin

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