2016-03-03 10 views
22

Các lời xin lỗi trước: câu hỏi này đến từ một nhà phát triển C++ lõi cứng, không được cải thiện cố gắng học C# nâng cao. Hãy xem xét những điều sau:Là C# 6? (Elvis op) chủ đề an toàn? Nếu vậy, làm thế nào?

if (myUserDefinedObject != null) 
{ 
    myUserDefinedObject.ToString(); 
} 

Đây rõ ràng không phải là chủ đề an toàn. Mặt khác, tôi đã nhìn thấy hai hướng dẫn nói? (Các Null Conditional Operator hoặc 'Elvis Operator') ví dụ,

myUserDefinedObject?.ToString(); 

chủ đề an toàn. Trừ khi trình biên dịch kết thúc tốt đẹp một [mutex?] Khóa xung quanh nó dưới nắp (run rẩy), tôi không hiểu làm thế nào mà có thể đúng. Nếu thành ngữ này là thread an toàn, ai đó có thể chỉ cho tôi một mô tả kỹ thuật về cách thức đó được thực hiện? Nếu nó không phải là thread an toàn, không ai có một tham chiếu mà thực sự nói nó không phải là?

+3

Xin lỗi, nhưng khối mã đầu tiên của bạn có thể hoàn toàn an toàn, tùy thuộc vào ngữ cảnh mà nó được sử dụng và phạm vi của các biến liên quan. –

+2

@KenWhite - Tôi nghĩ ý tưởng với khối đầu tiên là một luồng khác có thể đặt biến thành 'null' sau khi kiểm tra nhưng trước' .ToString() 'làm cho mã không thành công. Tôi sẽ nói rằng nó không phải là thread-safe. – Enigmativity

+0

@Enigmativity: Các poster đặc biệt nói rằng khối mã là ** không an toàn thread **, mà không phải là một tuyên bố chính xác mà không biết bối cảnh hoặc phạm vi. Tôi chỉ ra rằng tuyên bố là không chính xác - nó không đúng sự thật rằng mã trong khối đó dứt khoát không phải là luồng an toàn. –

Trả lời

24

Từ MSDN (tôi nhấn mạnh):

Một sử dụng cho việc tiếp cận thành viên vô điều kiện là cách gọi các đại biểu trong một cách thread-safe với mã ít hơn nhiều. Cách cũ đòi hỏi mã như sau:

var handler = this.PropertyChanged; 
if (handler != null) 
    handler(…) 

cách mới đơn giản hơn nhiều:

PropertyChanged?.Invoke(e) 

Cách mới là thread-an toàn vì trình biên dịch tạo ra mã để đánh giá PropertyChanged một lần duy nhất, giữ kết quả trong biến tạm thời.

Vì vậy, không có khóa liên quan đến vấn đề này.

+6

Vì vậy, làm thế nào an toàn thread nó phụ thuộc vào những gì bạn muốn làm. Bạn có thể nhận được một ObjectDisposedException nếu một thread khác gọi Dispose(), nhưng bạn sẽ không nhận được một ngoại lệ con trỏ null tại trang gọi. –

+0

Tôi rất muốn tin vào tài liệu MSDN nếu ai đó có thể giải thích cách thức, trong C#, giữa thời gian địa chỉ bộ nhớ kiểm tra không khác và thời gian trong tương lai không thể biết được khi cố gắng truy cập nội dung của địa chỉ đó, nội dung đó chưa được sửa đổi. Từ bài viết tôi trích dẫn ở trên, chúng tôi biết nó có thể được thực hiện bằng cách sử dụng phần cứng * để 'đánh dấu' vị trí đó (sử dụng nguyên tử, memory_modes, v.v.). Đó có phải là "trình biên dịch" đang làm gì không? @Hank, bạn cũng có thể nhận được câu trả lời - không phải từ đối tượng bạn mong đợi vẫn ở vị trí đó. ;-) –

+3

Đây không phải là chủ đề an toàn thực sự như [được giải thích tại đây] (https://msdn.microsoft.com/en-us/magazine/jj883956.aspx) tham khảo phần "Đọc Giới thiệu". JIT đôi khi có thể loại bỏ hoàn toàn biến cục bộ và giới thiệu một lần đọc khác đến trường thực tế. –

28

Tôi muốn làm rõ câu trả lời của BJ Myers (đúng).

Trong C# một sự kiện có thể được coi như một trường kiểu đại biểu - giống như một thuộc tính có thể được coi là trường thuộc loại thuộc tính - và giá trị của trường đó có thể là rỗng. Nếu bạn ở trong tình huống không may có một trình xử lý sự kiện đang được sửa đổi trên một chuỗi trong khi một chuỗi khác đang cố gọi nó, bạn có thể vào tình huống:

if (this.SomeEvent != null) 
    this.SomeEvent(...); 

không an toàn. Giá trị có thể được biến đổi để nó không phải là null trước khi kiểm tra, null sau khi kiểm tra, và chương trình bị treo.

Cách thông thường để tạo "chủ đề an toàn" này và tôi sử dụng cụm từ được khuyên dùng, là sao chép giá trị vào một địa phương và sau đó kiểm tra giá trị cục bộ cho giá trị rỗng. Điều này có lợi ích của việc không bị đổ vỡ với một dereference null. Tuy nhiên, các nhà phát triển thông minh sẽ lưu ý rằng vẫn còn một cuộc đua!Chuỗi có thể

  • Non-null xử lý sự kiện được lưu trữ trên thread
  • Event handler thiết lập để null trên thread B
  • Nhà nước cần thiết cho xử lý sự kiện bị hủy diệt trên thread B
  • Event handler chạy trên chủ đề A và chết khủng khiếp

Vì vậy, theo nghĩa đó, mẫu này không phải là "an toàn chủ đề". Nếu bạn đang ở vị trí không may này bạn chịu trách nhiệm đảm bảo rằng logic luồng thích hợp được triển khai để điều này không thể xảy ra. Bạn có thể làm điều đó tuy nhiên bạn muốn. Nếu bạn muốn các lợi ích (có thể nghi ngờ) có thể gọi trình xử lý sự kiện trên một chuỗi trong khi thay đổi sự kiện trên một chuỗi khác, thì bạn phải trả tiền để đảm bảo an toàn hoặc xử lý lỗi điều kiện chủng tộc.

Cá nhân tôi sẽ tránh tình trạng này như bệnh dịch hạch, nhưng tôi không đủ thông minh để viết mã đa luồng chính xác.

Bây giờ, đối với câu hỏi thực tế:

some_expression ?. ToString(); 

cũng giống như

temp = some_expression 
temp == null ? null : temp.ToString() 

Là mã sau "thread" theo ý kiến ​​của bạn?

+0

Ý kiến ​​tuyệt vời ở đây - cảm ơn lời giải thích tuyệt vời của bạn. "Chủ đề an toàn" phụ thuộc hoàn toàn vào cách mà các chủ đề đang sử dụng mã. –

+0

Vậy ... KHÔNG phải là đi bộ an toàn? hoặc là nó? Chúng ta có cần sử dụng khóa để tránh sự cố nếu chúng ta có nhiều luồng lộn xộn xung quanh không? – Narvalex

+4

@Narvalex: Tôi nên biết bằng cách nào? Giải thích * nơi bạn đang đặt khóa * và * những gì cuộc đua khóa bảo vệ chống lại *, và * chiến lược của bạn là để tránh deadlocks *, và sau đó chúng tôi có thể có một số shot để xác định xem giải pháp của bạn là chính xác. Hãy nhớ rằng, * thread an toàn là một tài sản của toàn bộ chương trình *, vì vậy nếu bạn muốn biết liệu một chương trình là threadsafe, bạn phải cung cấp một precis của toàn bộ chương trình. * Đó là lý do tại sao luồng là khó *. Bạn đang cầm một viên gạch và hỏi "ngôi nhà có phải viên gạch này được tạo ra từ âm thanh có cấu trúc không?" Đó là tài sản của ngôi nhà, không phải gạch. –

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