2008-11-02 46 views
5

Một mẫu phổ biến trong C++ là tạo một lớp bao bọc một khóa - khóa hoặc là được lấy ngầm khi đối tượng được tạo ra, hoặc được chụp một cách rõ ràng sau đó. Khi đối tượng đi ra khỏi phạm vi, dtor tự động phát hành khóa. Có thể thực hiện điều này trong C# không? Theo như tôi hiểu không có đảm bảo về khi dtor trong C# sẽ chạy sau khi đối tượng đi ra khỏi phạm vi.Có thể thực hiện khóa bị khóa trong C# không?

Làm rõ: Bất kỳ khóa nào nói chung, spinlock, ReaderWriterLock, bất cứ điều gì. Gọi Vứt bỏ bản thân mình đánh bại mục đích của mẫu - để có khóa được giải phóng ngay sau khi chúng tôi thoát khỏi phạm vi - không có vấn đề gì nếu chúng ta gọi trở lại ở giữa, ném ngoại lệ hoặc không có điều gì. Ngoài ra, theo như tôi hiểu bằng cách sử dụng sẽ vẫn chỉ xếp hàng đối tượng cho GC, không phá hủy nó ngay lập tức ...

Trả lời

5

Hiểu biết của bạn về using là không chính xác, đây là cách để hành động phạm vi xảy ra theo kiểu xác định (không xếp hàng với GC diễn ra).

C# cung cấp từ khóa lock cung cấp khóa độc quyền và nếu bạn muốn có các loại khác nhau (ví dụ: Đọc/ghi), bạn sẽ phải sử dụng câu lệnh using.

P.S. This thread có thể bạn quan tâm.

4

Đúng là bạn không biết chính xác khi nào dtor sẽ chạy ... nhưng, nếu bạn thực hiện giao diện IDisposable, và sau đó sử dụng hoặc là một 'sử dụng' khối hoặc gọi 'Vứt bỏ()' chính mình, bạn sẽ có một nơi để đặt mã của bạn.

Câu hỏi: Khi bạn nói "khóa", bạn có nghĩa là khóa chuỗi sao cho chỉ có một luồng tại một thời điểm có thể sử dụng đối tượng? Như trong:

lock (_myLockKey) { ... } 

Hãy làm rõ.

12

Để khuếch đại câu trả lời của Timothy, báo cáo khóa sẽ tạo khóa khóa bằng cách sử dụng màn hình. Về cơ bản, điều này được dịch thành một cái gì đó như thế này:

lock(_lockKey) 
{ 
    // Code under lock 
} 

// is equivalent to this 
Monitor.Enter(_lockKey) 
try 
{ 
    // Code under lock 
} 
finally 
{ 
    Monitor.Exit(_lockKey) 
} 

Trong C# bạn hiếm khi sử dụng dtor cho kiểu mẫu này (xem câu lệnh sử dụng/IDisposable). Một điều bạn có thể nhận thấy trong mã là nếu một ngoại lệ async xảy ra giữa Monitor.Enter và thử, có vẻ như màn hình sẽ không được phát hành. JIT thực sự bảo đảm đặc biệt rằng nếu một Monitor.Enter ngay lập tức đứng trước một khối thử, ngoại lệ async sẽ không xảy ra cho đến khi khối try đảm bảo việc giải phóng.

3

Để có đầy đủ, có một cách khác để đạt được hiệu ứng RAII tương tự mà không cần sử dụng usingIDisposable. Trong C# using thường rõ ràng hơn (see also here để biết thêm một số suy nghĩ), nhưng bằng các ngôn ngữ khác (ví dụ: Java) hoặc thậm chí trong C# nếu using không phù hợp vì một số lý do, điều này rất hữu ích.

Đó là thành ngữ được gọi là "Thực thi xung quanh" và ý tưởng là bạn gọi phương thức làm công cụ trước và sau (ví dụ: khóa/mở khóa chủ đề của bạn hoặc thiết lập và cam kết/đóng kết nối DB của bạn, v.v.) bạn chuyển vào phương thức đó một đại biểu sẽ thực hiện các hoạt động bạn muốn xảy ra ở giữa.

ví dụ::


funkyObj.InOut(delegate{ System.Console.WriteLine("middle bit"); }); 

Tùy thuộc vào những gì các phương pháp InOut làm, sản lượng có thể là một cái gì đó như:

 
first bit 
middle bit 
last bit 

Như tôi đã nói, câu trả lời này là cho đầy đủ mà thôi, những gợi ý trước của using với IDisposable, như cũng như từ khóa lock, sẽ tốt hơn 99% thời gian.

Thật đáng tiếc, trong khi .Net đã đi xa hơn nhiều ngôn ngữ OO hiện đại khác trong vấn đề này (tôi đang nhìn bạn, Java), nó vẫn đặt trách nhiệm cho RAII làm việc trên mã máy khách (ví dụ: mã sử dụng using), trong khi trong C++ trình phá hủy sẽ luôn chạy ở cuối phạm vi.

0

Tôi đã thực sự bị làm phiền bởi thực tế rằng using là tùy thuộc vào nhà phát triển cần nhớ - tốt nhất là bạn nhận được cảnh báo, điều mà hầu hết mọi người không bao giờ bận tâm để quảng bá lỗi. Vì vậy, tôi đã được chơi đùa với một ý tưởng như thế này - nó buộc khách hàng ít nhất là TRY để làm những việc chính xác. May mắn thay và không may, đó là một đóng cửa, vì vậy khách hàng vẫn có thể giữ một bản sao của tài nguyên, và cố gắng sử dụng nó một lần nữa - nhưng mã này ít nhất cố gắng đẩy khách hàng đi đúng hướng ...

public class MyLockedResource : IDisposable 
{ 
    private MyLockedResource() 
    { 
     Console.WriteLine("initialize"); 
    } 

    public void Dispose() 
    { 
     Console.WriteLine("dispose"); 
    } 

    public delegate void RAII(MyLockedResource resource); 

    static public void Use(RAII raii) 
    { 
     using (MyLockedResource resource = new MyLockedResource()) 
     { 
      raii(resource); 
     } 
    } 

    public void test() 
    { 
     Console.WriteLine("test"); 
    } 
} 

Cách sử dụng tốt:

MyLockedResource.Use(delegate(MyLockedResource resource) 
{ 
    resource.test(); 
}); 

Sử dụng kém! (Rất tiếc, không thể ngăn chặn điều này ...)

MyLockedResource res = null; 
MyLockedResource.Use(delegate(MyLockedResource resource) 
{ 
    resource.test(); 
    res = resource; 
    res.test(); 
}); 
res.test(); 
Các vấn đề liên quan