2012-12-26 19 views
6

Tôi thích cách tạo nhanh các lớp trong C# nơi chúng ta có thể dễ dàng tạo các Thành viên mà không phải triển khai mã nhận và đặt nhưng (theo như tôi biết) chúng không phải là chủ đề an toàn!Bất kỳ cách nào để làm cho chuỗi C# lớp này an toàn mà không cần viết lại?

public class SocksEntry 
{ 
    public int Int1 { get; set; } 
    public int Int2 { get; set; } 
} 

C# có cung cấp cách nhanh chóng để thêm an toàn luồng mà không phải thực hiện việc này không?

public class SocksEntry 
{ 
    protected object _lock = new object(); 

    // internal 
    private int _int1 = 0; 
    private int _int2 = 0; 

    // accessors 
    protected int Int1 
    { 
     get { lock (_lock) return _int1; } 
     set { lock (_lock) _int1 = value; } 
    } 

    protected int Int2 
    { 
     get { lock (_lock) return _int2; } 
     set { lock (_lock) _int2 = value; } 
    } 
} 

Nó rõ ràng làm cho toàn bộ lớp lớn hơn rất nhiều và một nỗi đau để tạo ra so với phiên bản an toàn không đề!

+0

Có thể muốn xem nội dung này: http://www.codeproject.com/Articles/33559/Handy-wrapper-class-for-thread-safe-property-acces –

+1

Mã của bạn với ổ khóa được nhúng trong thuộc tính accessors cũng không thực sự an toàn cho thread. Điều duy nhất họ có thể mua cho bạn là rào cản bộ nhớ nếu các phương pháp được gạch chân, nhưng các hoạt động bạn đang 'khóa' xung quanh thực sự là các hoạt động nguyên tử. Nó sẽ chỉ có ý nghĩa để khóa trên các hoạt động cấp cao hơn liên quan đến sự phối hợp của nhiều tiểu bang. –

Trả lời

7

Làm cho chủ đề lớp an toàn khó hơn rất nhiều so với bạn thậm chí có thể tưởng tượng. Nó có thể không đủ để đặt ổ khóa xung quanh getters và setters. Vì vậy, thực hành tốt nhất là để có lớp an toàn không thread và để lại trách nhiệm cho người tiêu dùng của lớp này để đồng bộ hóa truy cập vào nó nếu anh ta cần an toàn thread. Nếu người tiêu dùng không cần an toàn luồng, thì tuyệt vời, bạn sẽ không bị phạt hiệu suất của ứng dụng chỉ vì bạn đã xây dựng an toàn luồng vào lớp mà thậm chí không cần đến nó. Tại sao bạn nghĩ rằng 99,99% các lớp trong .NET không an toàn cho luồng?

+0

Bạn có thể chứng minh hay không? – IamStalker

+2

Chắc chắn, tôi có thể. Tất cả những gì bạn phải làm là loại bỏ các khóa trong ví dụ nói trên. Hãy xem đoạn mã đầu tiên mà OP cung cấp. Đó là cách lớp trông giống như trong hầu hết các trường hợp. Theo như tiêu thụ lớp này trong một chủ đề an toàn chủ đề là có liên quan, tốt, điều này sẽ hoàn toàn phụ thuộc vào bối cảnh. OP đã không thực sự xác định cách lớp này được sử dụng vì vậy nó là một chút khó khăn để cung cấp các ví dụ cụ thể mà không có điều này. –

+0

Ohh bạn bỏ lỡ hiểu tôi tôi muốn xem một chủ đề an toàn, giải pháp. – IamStalker

1

Trong một từ, không.

Thuộc tính được tự động triển khai rất tuyệt, nhưng khi một lần truy cập vào các giới hạn, chính là vậy.

Tuy nhiên, bạn có thể tạo Visual Studio snippet (như prop) để viết mã đĩa nồi hơi cho bạn.

0

Tôi nghĩ vấn đề an toàn thread bạn đang đề cập đến là một cái gì đó như thế này:

public int Value { get; set; } 

public void Foo() 
{ 
    if (this.Value > 0) // Read the property and make a decision based on its present value 
    { 
     // Do something with the property, assuming its value has passed your test. 
     // Note that the property's value may have been changed by another thread between the previous line and this one. 
     Console.WriteLine(this.Value); 
    } 
} 

Vấn đề là giá trị tài sản có thể đã thay đổi từ khi bạn kiểm tra nó và khi bạn sử dụng nó. Điều này xảy ra cho dù bạn sử dụng các thuộc tính tự động, các biến sao lưu hoặc các khóa bạn có trong câu hỏi của mình. Giải pháp chính xác phụ thuộc vào cách bạn cần ứng dụng của bạn hoạt động (nó nên làm gì nếu giá trị đã thay đổi giữa một dòng này và dòng khác). Giải pháp phổ biến là lưu vào bộ nhớ cache giá trị ngay trong hàm mà bạn đang sử dụng nó:

public int Value { get; set; } 

public void Foo() 
{ 
    int cachedValue = this.Value; 

    if (cachedValue > 0) 
    { 
     Console.WriteLine(cachedValue); 
    } 
} 

Tuy nhiên, điều đó không nhất thiết phải đúng với những gì ứng dụng của bạn phải làm.

0

Cách dễ dàng nhất và đúng hơn là cách tiếp cận đúng duy nhất là tạo lớp học immutable, do đó chỉ có các thao tác đọc có sẵn.

public sealed class SocksEntry 
{ 
    public int Int1 { get; private set; } 
    public int Int2 { get; private set; } 
} 

Nhưng nếu điều này là không thể cho một thực thể kinh doanh cụ thể - tiếp xúc với một số phương pháp kinh doanh chứ không phải là setters tài sản, sau đó nó là dễ dàng hơn nhiều để đồng bộ hóa toàn bộ phương pháp (nghĩ giao dịch) chứ không phải mỗi setter tài sản mà đôi khi làm cho tinh thần thiết alltogether

// lock entire business transaction if possible 
public void UpdateEntity(int a, int b) 
{ 
    lock (entityLock) 
    { 
     Int1 = a; 
     Int2 = b; 
    } 
}  
+1

Làm cho một lớp học không thay đổi không làm cho nó an toàn thread trong trường hợp chung. Trong trường hợp cụ thể với 2 thuộc tính nguyên có thể, nhưng hãy tưởng tượng một thuộc tính kiểu 'List ' chẳng hạn. Bây giờ ngay cả khi bạn đã làm cho nó chỉ đọc, không có gì ngăn cản người tiêu dùng của lớp này gọi getter trong danh sách và sau đó từ một chủ đề thêm các mục vào danh sách và từ một chủ đề lặp qua danh sách cùng một lúc. Như bạn biết 'Danh sách ' không phải là một loại an toàn thread và bạn gặp rắc rối nếu bạn không đồng bộ hóa quyền truy cập vào danh sách này từ bên ngoài. –

+0

Tôi nghĩ rằng ví dụ của bạn với 'Danh sách ' không phải là một ví dụ về lớp bất biến kể từ khi bạn nói bạn vẫn không thể cập nhật danh sách – sll

+0

Có, nhưng đó là đúng đối với bất kỳ loại tham chiếu nào. Khi bạn nhận được tham chiếu đến loại, bạn có thể sửa đổi nó. Vì vậy, ví dụ của bạn với các loại bất biến chỉ áp dụng cho các loại giá trị. Làm cách nào để bạn xử lý các loại tham chiếu trong trường hợp này? –

0

trong mô hình bộ nhớ NET, đọc và viết các loại nguyên bản nhỏ hơn hoặc bằng với kích thước của con trỏ của nền tảng của bạn (ví dụ int trên 32-bit, long trên 64-bit) là nguyên tử . Nguyên tử có nghĩa là bạn có thể đọc và viết nó mà không cần sử dụng khóa và bạn sẽ không bao giờ đọc hoặc viết giá trị không đầy đủ (tức là nửa đường giữa giá trị ghi).

Tuy nhiên,.NET không đảm bảo rằng việc viết sẽ được hiển thị ngay lập tức cho các chủ đề khác (có nghĩa là bạn có thể viết trong chủ đề A, sau đó đọc trong chủ đề B, và vẫn đọc một giá trị cũ). Trên x86 bạn có thể nhìn thấy ngay lập tức với kiến ​​trúc, nhưng trên các nền tảng khác, bạn cần phải sử dụng trường volatile hoặc Thread.MemoryBarrier sau khi viết. lock chèn một rào cản bộ nhớ cho bạn, vì vậy bạn không cần phải lo lắng về nó.

Một điều khác cần nghĩ đến là cách bạn sử dụng lớp học. Trong khi truy cập đơn lẻ là nguyên tử, thì vẫn cần có lock nếu bạn muốn đọc hoặc viết nhiều trường dưới dạng một hoạt động nguyên tử. (tốt, không phải về mặt kỹ thuật luôn luôn - có một số tối ưu nhất định mà đôi khi bạn có thể làm, nhưng dính vào những thứ đơn giản trong khi bạn đang học)

Vì vậy, trong ngắn hạn: lock có khá quá mức - bạn có thể loại bỏ nó và sử dụng dễ bay hơi/rào cản nếu bạn cần tầm nhìn ngay lập tức.

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