2012-02-09 25 views
8

Tôi muốn bắt đầu một số chủ đề mới cho mỗi thao tác lặp lại. Nhưng khi một hoạt động như vậy đã được tiến hành, tôi muốn loại bỏ nhiệm vụ hiện tại. Trong kịch bản của tôi, tôi chỉ cần dữ liệu rất hiện tại - dữ liệu bị loại bỏ không phải là vấn đề.Khóa không chặn

Trong MSDN tôi đã tìm thấy lớp Mutex nhưng khi tôi hiểu nó, nó chờ đến lượt nó, chặn luồng hiện tại. Ngoài ra tôi muốn hỏi bạn: Có một cái gì đó tồn tại trong khuôn khổ NET đã có, mà sau:

  1. là một số phương pháp M đã được thực thi?
  2. Nếu vậy, return (và cho tôi tăng số truy cập cho các thống kê)
  3. Nếu không, hãy bắt đầu phương pháp M trong một thread mới

Trả lời

23

Tuyên bố lock(someObject), mà bạn có thể gặp phải, là cú pháp đường xung quanh Monitor.EnterMonitor.Exit.

Tuy nhiên, nếu bạn sử dụng màn hình theo cách tiết kiệm hơn này, bạn cũng có thể sử dụng Monitor.TryEnter cho phép bạn kiểm tra xem bạn có thể nhận khóa hay không - do đó kiểm tra xem ai đó đã có và đang thực thi mã .

Vì vậy, thay vì điều này:

var lockObject = new object(); 

lock(lockObject) 
{ 
    // do some stuff 
} 

thử này (phương án 1):

int _alreadyBeingExecutedCounter; 
var lockObject = new object(); 

if (Monitor.TryEnter(lockObject)) 
{ 
    // you'll only end up here if you got the lock when you tried to get it - otherwise you'll never execute this code. 

    // do some stuff 

    //call exit to release the lock 
    Monitor.Exit(lockObject); 
} 
else 
{ 
    // didn't get the lock - someone else was executing the code above - so I don't need to do any work! 
    Interlocked.Increment(ref _alreadyBeingExecutedCounter); 
} 

(có thể bạn sẽ muốn đặt một try..finally trong đó để đảm bảo tính khóa được phát hành)

hoặc phân phối với khóa rõ ràng althogether và làm điều này

(tùy chọn 2)

private int _inUseCount; 

public void MyMethod() 
{ 
    if (Interlocked.Increment(ref _inUseCount) == 1) 
    { 
     // do dome stuff  
    } 
    Interlocked.Decrement(ref _inUseCount); 
} 

[Edit: để đáp lại câu hỏi của bạn về this]

Không - không sử dụng this để lock trên. Tạo một đối tượng có phạm vi riêng tư để hoạt động như khóa của bạn.

Nếu không, bạn có vấn đề tiềm năng này:

public class MyClassWithLockInside 
{ 
    public void MethodThatTakesLock() 
    { 
     lock(this) 
     { 
      // do some work 
     } 
    } 
} 

public class Consumer 
{ 
    private static MyClassWithLockInside _instance = new MyClassWithLockInside(); 

    public void ThreadACallsThis() 
    { 
      lock(_instance) 
      { 
       // Having taken a lock on our instance of MyClassWithLockInside, 
       // do something long running 
       Thread.Sleep(6000); 
      } 
    } 

    public void ThreadBCallsThis() 
    { 
     // If thread B calls this while thread A is still inside the lock above, 
     // this method will block as it tries to get a lock on the same object 
     // ["this" inside the class = _instance outside] 
     _instance.MethodThatTakesLock(); 
    } 
} 

Trong ví dụ trên, một số mã bên ngoài đã quản lý để phá vỡ các khóa nội bộ của lớp chúng tôi chỉ bằng cách lấy ra một khóa trên cái gì đó đã từ bên ngoài truy cập.

Tốt hơn hết là tạo đối tượng riêng tư mà bạn kiểm soát và không có ai ngoài lớp học của bạn có quyền truy cập, để tránh những vấn đề này; điều này bao gồm không sử dụng this hoặc loại chính nó typeof(MyClassWithLockInside) để khóa.

+0

Sẽ 'this' này là một lockObject phù hợp? – DerMike

+2

Tại sao lại là downvote? –

+1

Được chấp nhận cho tùy chọn 2 – DerMike

1

Bạn có thể tìm thấy một số thông tin hữu ích trên site sau (PDF là cũng có thể downlaodable - gần đây đã tự tải xuống). Các chương trình Tạm ngừng và Tiếp tục hoặc Hủy bỏ có thể là những gì bạn đang inetrested in.

+0

Tôi sẽ đọc toàn bộ bài viết, tôi đã tìm thấy nó không thể thiếu, http://www.albahari.com/threading/ từ đầu – Robert

2

Một lựa chọn sẽ được làm việc với một sentinel reentrancy:

Bạn có thể xác định một lĩnh vực int (khởi tạo bằng 0) và cập nhật nó qua Interlocked.Increment trên vào phương pháp và chỉ tiến hành nếu nó là 1. Cuối cùng chỉ cần thực hiện một số Interlocked.Decrement.

Một tùy chọn khác:

Từ mô tả của bạn có vẻ như rằng bạn có một nhà sản xuất-tiêu dùng-Kịch bản ...

Đối với trường hợp này, nó có thể hữu ích để sử dụng một cái gì đó giống như BlockingCollection vì nó là thread-safe và hầu hết là lock-free ...

Một lựa chọn khác là sử dụng ConcurrentQueue hoặc ConcurrentStack ...

+0

Điểm tốt. Cảm ơn bạn. Điều gì sẽ lấy bên trông như thế nào? – DerMike

+0

@DerMike Tôi sẽ cung cấp cho bạn một ví dụ nhưng tôi sẽ cần thêm thông tin ... về cơ bản các bộ sưu tập này có phương thức 'Try' không chặn ... vui lòng kiểm tra cập nhật của tôi ở trên về cách sử dụng' Interlocked' ... – Yahia

0

Bạn nên sử dụng các hoạt động nguyên tử lớp đan cài - for best performance - vì bạn sẽ không thực sự sử dụng các hệ thống đồng bộ cấp hệ thống (bất kỳ nguyên thủy "chuẩn" nào cần nó và liên quan đến phí gọi hệ thống). // mutex đơn giản không reentrant mà không có quyền sở hữu, dễ dàng làm lại để hỗ trợ // các tính năng này (chỉ cần thiết lập chủ sở hữu sau khi mua khóa (so sánh Thread reference với Thread.CurrentThread ví dụ), và kiểm tra danh tính phù hợp, thêm counter cho reentrancy) // không thể sử dụng bool vì nó không được khóa CompareExchange khóa riêng tư hỗ trợ;

public bool TryLock() 
{ 
    //if (Interlocked.Increment(ref _inUseCount) == 1)  
    //that kind of code is buggy - since counter can change between increment return and 
    //condition check - increment is atomic, this if - isn't.  
    //Use CompareExchange instead 
    //checks if 0 then changes to 1 atomically, returns original value 
    //return true if thread succesfully occupied lock 
    return CompareExchange(ref lock, 1, 0)==0; 
    return false; 

} 
public bool Release() 
{ 
    //returns true if lock was occupied; false if it was free already 
    return CompareExchange(ref lock, 0, 1)==1; 
} 
Các vấn đề liên quan