2015-05-28 13 views
5

Tôi có các tình huống sau:Cải thiện vấn đề khóa cần

Tôi đang cố gắng để khóa một sợi tại chỗ, nếu đó là chủ đề 'tùy chỉnh' id phù hợp với một trong đó đã bước vào phần khóa tắt mã, nhưng không nếu id khác.

Tôi tạo ra một số mẫu mã để giải thích hành vi của tôi muốn

class A 
{   
    private static Dictionary<int, object> _idLocks = new Dictionary<int, object>(); 
    private static readonly object _DictionaryLock = new object(); 
    private int _id; 

    private void A (int id) 
    { 
     _id = id; 
    } 

    private object getObject() 
    { 
     lock (_DictionaryLock) 
     { 
      if (!_idLocks.ContainsKey(_id)) 
       _idLocks.Add(_id, new object()); 
     } 
     lock (_idLocks[_id]) 
     { 
      if (TestObject.Exists(_id)) 
       return TestObject(_id); 
      else 
       return CreateTestObject(_id); 
     } 
    } 
} 

Bây giờ công trình này 100% cho những gì tôi mở rộng, nơi id ví dụ 1 không kiểm tra để xem nếu đối tượng của nó đã được tạo ra trong khi người khác thread với id 1 đã bận rộn tạo đối tượng đó.

Nhưng có hai khóa và từ điển tĩnh dường như không đúng cách, vì vậy tôi hy vọng một người nào đó có thể chỉ cho tôi phương pháp ngăn chặn chuỗi truy cập chỉ khi chuỗi đó được tạo bằng cùng một id như id đã bận rộn thực thi mã trong phần bị khóa.

Tôi đã nhìn vào lớp ReaderWriterLockSlim nhưng với tôi nó không thực sự có ý nghĩa để được sử dụng vì tôi không muốn đối tượng TestObject (id) được đọc ở tất cả trong khi nó vẫn đang được tạo ra.

Tôi không quan tâm đến việc khóa chuỗi khi truy cập từ điển. Những gì tôi đang cố gắng tránh ở tất cả các chi phí là _id mà thread chạy không nên được sử dụng bên trong CreateTestObject(_id) trong khi đã có một bận rộn, bởi vì các tập tin đang được tạo và xóa với id đó sẽ ném ngoại lệ nếu hai chủ đề đang cố gắng để truy cập vào cùng một tệp

Chỉ có thể sửa được bằng khóa bình thường, nhưng trong trường hợp này, tôi vẫn muốn một chuỗi có _id hiện không chạy bên trong phương thức CreateTestObject(_id) để có thể nhập mã trong khóa.

Điều này là tất cả bởi vì những gì xảy ra bên trong CreateTestObject cần thời gian và hiệu suất sẽ bị ảnh hưởng nếu chuỗi đang chờ truy cập.

+0

Có thể thực hiện khóa trên toàn bộ từ điển '_idLocks' thay vì có 2 khóa không? Tôi khá mới để khóa vì vậy tôi không chắc chắn những gì bạn có thể sử dụng như một đối tượng khóa hay không. –

Trả lời

3

Có vẻ như bạn đang sử dụng mã này để điền từ điển theo cách an toàn theo chủ đề - bạn có thể sử dụng số ConcurrentDictionary thay thế không?

class A { 
    private static ConcurrentDictionary<int, object> _dictionary = new ConcurrentDictionary<int, object>(); 

    private int _id; 

    private object GetObject() { 
    object output = null; 
    if(_dictionary.TryGetValue(_id, output)) { 
     return output; 
    } else { 
     return _dictionary.GetOrAdd(_id, CreateTestObject(_id)); 
    } 
    } 
} 

Edit: Nếu bạn muốn loại bỏ hoàn toàn khả năng gọi lặp lại CreateTestObject phương pháp sau đó bạn có thể lưu trữ một wrapper trong _dictionary mà uể oải đặt object

class Wrapper { 
    private volatile object _obj = null; 

    public object GetObj() { 
    while(_obj == null) { 
     // spin, or sleep, or whatever 
    } 
    return _obj; 
    } 

    public void SetObj(object obj) { 
    _obj = obj; 
    } 
} 

class A { 
    private static ConcurrentDictionary<int, Wrapper> _dictionary = new ConcurrentDictionary<int, Wrapper>(); 

    private int _id; 

    private object GetObject() { 
    Wrapper wrapper = null; 
    if(_dictionary.TryGetValue(_id, wrapper)) { 
     return wrapper.GetObj(); 
    } else { 
     Wrapper newWrapper = new Wrapper(); 
     wrapper = _dictionary.GetOrAdd(_id, newWrapper); 
     if(wrapper == newWrapper) { 
     wrapper.SetObj(CreateTestObject(_id)); 
     } 
     return wrapper.GetObj(); 
    } 
    } 
} 

Chỉ một thread sẽ có thể để đặt Wrapper mới trong _dictionary tại số _id được chỉ định - chuỗi đó sẽ khởi tạo đối tượng bên trong điều kiện wrapper == newWrapper. Wrapper#GetObj quay cho đến khi đối tượng được đặt, điều này có thể được viết lại để chặn thay thế.

+0

Tôi không biết có một 'ConcurrentDictionary'. +1! –

+0

Xin chào, tôi đã cập nhật câu hỏi ban đầu của mình để rõ ràng hơn, cảm ơn sự hỗ trợ của bạn cho đến nay – Domitius

+0

@Domitius Tôi đã cập nhật câu trả lời của mình –

0

Điều này không thể hoạt động, bởi vì Monitor (được sử dụng nội bộ theo tuyên bố lock) là người tham gia lại. Điều đó có nghĩa là một chuỗi có thể nhập bất kỳ khóa nào mà nó đã sở hữu bất kỳ số lần nào.

Bạn có thể giải quyết điều này bằng cách sử dụng Semaphore thay vì Monitor, nhưng dừng lại một lúc và lắng nghe những gì bạn đang yêu cầu - bạn muốn chuỗi chặn trên số lock được sở hữu bởi cùng một chuỗi đó.Làm thế nào là thread bao giờ sẽ thức dậy? Nó sẽ bế tắc mãi mãi - chờ đợi cho các lock được phát hành, trong khi cũng là một trong những giữ lock.

Hoặc bạn chỉ đang cố xử lý sự khởi tạo lười biếng của một số đối tượng mà không phải chặn tất cả các chủ đề khác? Đó là thực sự khá đơn giản:

ConcurrentDictionary<int, YourObject> dictionary; 

return dictionary.GetOrAdd(id, i => CreateTestObject(i)); 

Lưu ý rằng CreateTextObject được gọi là chỉ nếu chìa khóa không tồn tại trong từ điển được nêu ra.

+0

Xin chào, tôi đã cập nhật câu hỏi ban đầu của mình để rõ ràng hơn, cảm ơn sự hỗ trợ của bạn cho đến nay – Domitius

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