2010-06-05 40 views
5

Tôi thường thấy mã như vậy là shown here, tức là nơi một đối tượng được phân bổ và sau đó được sử dụng làm "đối tượng khóa".Khóa trên một đối tượng

Dường như với tôi rằng bạn có thể sử dụng bất kỳ đối tượng nào cho điều này, bao gồm cả sự kiện chính nó làm đối tượng khóa. Tại sao phân bổ một đối tượng mới mà không có gì? Sự hiểu biết của tôi là gọi khóa() trên một đối tượng không thực sự thay đổi đối tượng, cũng không thực sự khóa nó khỏi sử dụng, nó đơn giản được sử dụng như một trình giữ chỗ cho nhiều câu lệnh khóa để neo.

public class Shape : IDrawingObject, IShape 
{ 
    // Create an event for each interface event 
    event EventHandler PreDrawEvent; 
    event EventHandler PostDrawEvent; 

    object objectLock = new Object(); 

    // Explicit interface implementation required. 
    // Associate IDrawingObject's event with 
    // PreDrawEvent 
    event EventHandler IDrawingObject.OnDraw 
    { 
     add 
     { 
      lock (objectLock) 
      { 
       PreDrawEvent += value; 
      } 
     } 
     remove 
     { 
      lock (objectLock) 
      { 
       PreDrawEvent -= value; 
      } 
     } 
    } 
} 

Vì vậy, câu hỏi của tôi là, đây thực sự là một điều tốt để làm?

+0

(xem cập nhật, btw) –

Trả lời

7

bao gồm các sự kiện riêng của mình

Không, bạn không thể làm điều đó. Một "sự kiện" thực sự chỉ là một số phương thức truy cập. Giả sử bạn có nghĩa là đại biểu ủng hộ, điều đó sẽ rất tệ - các đại biểu không thay đổi được: mỗi khi bạn thêm/xóa người đăng ký, bạn sẽ nhận được các đại biểu khác nhau.

Thực ra, trình biên dịch 4.0 hiện thực hiện điều này với mã không khóa bằng cách sử dụng Interlocked - có thể đáng giá theo cách tiếp cận này thay thế.

Trong ví dụ của bạn, objectLock đảm bảo rằng tất cả người gọi (để dụ đó) là khóa chống cùng đối tượng, đó là quan trọng - nhưng không có sự xấu xí của khóa trên this (đó là cách biên dịch C# sử dụng làm việc).

-

Cập nhật: ví dụ của bạn hiển thị mã số đó là cần thiết trước khi C# 4.0, truy cập vào một lĩnh vực như sự kiện bên kiểu nói chuyện trực tiếp đến lĩnh vực: lĩnh vực như sự kiện bình thường khóa không được tôn trọng. Điều này đã được thay đổi trong C# 4.0; bạn có thể bây giờ (trong C# 4.0) an toàn lại viết những dòng này như:

public class Shape : IDrawingObject, IShape 
{ 
    // Create an event for each interface event 
    event EventHandler PreDrawEvent; 
    event EventHandler PostDrawEvent; 

    event EventHandler IDrawingObject.OnDraw 
    { 
     add { PreDrawEvent += value; } 
     remove { PreDrawEvent -= value; } 
    } 
} 

Tất cả các hành vi đúng là sau đó theo sau.

+0

Vì vậy, bạn đang nói rằng nếu khóa (PreDrawEvent) sẽ dẫn đến một đối tượng khác nhau bị khóa mỗi khi đại biểu được thay đổi? Ugh .. Điều đó không có vẻ rất .. trực quan. Tôi đã luôn luôn giả định rằng các sự kiện là container, womewhat như List <> có chứa các đại biểu .. là một giả định sai? –

+0

@Mystere Man - tệ hơn: sự kiện chưa được đăng ký là 'null', vì vậy nó sẽ thất bại hoàn toàn. Giả định của bạn là không chính xác. –

+1

Giả định khi nói đến khóa là một ý tưởng thực sự tồi. Biết chính xác cách thức hoạt động của khóa là tối quan trọng. – Rusty

1

Bạn nên khóa trên một trường tĩnh riêng vì điều này đảm bảo rằng nhiều luồng cố gắng truy cập khóa song song sẽ bị chặn. Khóa trên thể hiện của chính lớp đó (lock(this)) hoặc một số trường thể hiện có thể có vấn đề bởi vì nếu hai luồng gọi phương thức trên hai trường hợp khác nhau của đối tượng, chúng sẽ có thể đồng thời nhập vào câu lệnh khóa.

+0

Có, nhưng đó không phải là những gì tôi đã nói. Tôi đã đề cập đến việc sử dụng thành viên sự kiện riêng tư như là biến khóa (ví dụ: PreDrawEvent). Tôi cũng nên lưu ý rằng ví dụ không sử dụng biến tĩnh. Bên cạnh đó, khóa trên điều này sẽ có vấn đề nếu bạn có nhiều hơn một tuyên bố khóa trong lớp. –

+0

Điều tương tự: khóa trên 'this' hoặc' instance field' có thể xấu vì trường instance sẽ thay đổi tùy thuộc vào cá thể của đối tượng trong khi trường readonly tĩnh sẽ không bao giờ thay đổi. –

+0

Tôi thực sự không thấy vấn đề là gì. Toàn bộ vấn đề là khóa một thể hiện của một biến để nhiều luồng không thể thay đổi cá thể đó. Tại sao bạn muốn ngăn chặn hai trường hợp khác nhau khỏi bị sửa đổi đồng thời? –

2

Bất kỳ thành viên loại tham chiếu riêng tư nào cũng sẽ thực hiện công việc. Miễn là nó là riêng tư và không bao giờ được phân công lại. Mà gõ một đối tượng đại biểu ra khỏi chạy, bạn chắc chắn không muốn nhìn thấy một khóa không làm công việc của mình chỉ đơn giản bởi vì mã khách hàng mà bạn không kiểm soát gán một xử lý sự kiện. Vô cùng khó khăn để gỡ lỗi.

Sử dụng thành viên riêng tư thực hiện công việc khác không phải là thứ có quy mô tốt. Nếu bạn phát hiện ra bạn cần khóa một vùng mã khác trong khi tái cấu trúc hoặc gỡ lỗi, bạn sẽ cần tìm một thành viên riêng khác. Đó là nơi mọi thứ có thể biến đổi nhanh chóng: bạn có thể chọn lại cùng một thành viên riêng tư. Deadlock gõ cửa.

Điều này không xảy ra nếu bạn dành một đối tượng khóa cho một tập hợp các biến chia sẻ cụ thể cần được bảo vệ. Cho phép bạn cung cấp cho nó một cái tên tốt quá.

+0

Đây là lý do tôi đề xuất sử dụng sự kiện, vì đó là "đối tượng" mà bạn đang thao tác. Tuy nhiên, nếu tôi hiểu được ý kiến ​​của Marc một cách chính xác, thì việc sử dụng một sự kiện như một đối tượng khóa là một điều rất xấu ... –

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