2010-11-17 40 views
12

Tôi đang cố gắng để thực hiện đoạn mã sau vào một kịch bản đa luồng:Nhiều-độc giả, ổ khóa đơn nhà văn trong Boost

Get shared access to mutex 
Read data structure 
If necessary: 
    Get exclusive access to mutex 
    Update data structure 
    Release exclusive lock 
Release shared lock 

đề Boost có một lớp shared_mutex được thiết kế cho một nhiều độc giả, độc thân mô hình viết. Có một số câu hỏi stackoverflow liên quan đến lớp này. Tuy nhiên, tôi không chắc chắn nó phù hợp với kịch bản ở trên, nơi bất kỳ trình đọc nào có thể trở thành một người viết. Các tài liệu khẳng định:

Khái niệm UpgradeLockable là một sàng lọc của khái niệm SharedLockable cho phép quyền sở hữu nâng cấp cũng như chia sẻ quyền sở hữu và sở hữu độc quyền. Đây là một phần mở rộng đến nhiều-reader/ mô hình đơn ghi được cung cấp bởi các khái niệm SharedLockable : a đơn chủ đề có thể có quyền sở hữu upgradable cùng một lúc như những người khác đã chia sẻ sở hữu.

Từ chữ "đơn" Tôi nghi ngờ rằng chỉ một chuỗi có thể giữ khóa nâng cấp. Những người khác chỉ giữ một khóa chia sẻ mà không thể được nâng cấp lên khóa độc quyền.

Bạn có biết liệu boost::shared_lock có hữu ích trong trường hợp này không (bất kỳ người đọc nào có thể trở thành người viết) hoặc nếu có cách nào khác để đạt được điều này?

+0

Định nghĩa của bạn là 'quyền truy cập độc quyền' nếu không phải chỉ có một chủ đề có thể giữ khóa tại một thời điểm '? hoặc bạn đang bối rối 'một luồng tại một thời điểm' với 'một chuỗi cụ thể được chọn trước'? –

+0

@Pete: Tôi muốn một sợi (* không * được chọn trước) để giữ khóa độc quyền. – Amnon

Trả lời

5

boost::shared_lock không giúp trong tình huống này (nhiều độc giả có thể trở thành nhà văn), vì chỉ một chủ đề duy nhất có thể sở hữu một khóa nâng cấp. Đây là cả hai ngụ ý bởi các báo từ tài liệu trong câu hỏi, và bằng cách nhìn vào mã (thread \ win32 \ shared_mutex.hpp). Nếu một chủ đề cố gắng để có được một khóa nâng cấp trong khi một thread giữ một, nó sẽ chờ cho thread khác.

Tôi đã định sử dụng khóa thông thường cho tất cả người đọc/người viết, điều này có thể xảy ra trong trường hợp của tôi vì phần quan trọng là ngắn.

15

Có, bạn có thể thực hiện chính xác những gì bạn muốn như được hiển thị trong câu trả lời được chấp nhận here. Cuộc gọi để nâng cấp lên quyền truy cập độc quyền sẽ chặn cho đến khi tất cả người đọc hoàn thành.

boost::shared_mutex _access; 
void reader() 
{ 
    // get shared access 
    boost::shared_lock<boost::shared_mutex> lock(_access); 

    // now we have shared access 
} 

void writer() 
{ 
    // get upgradable access 
    boost::upgrade_lock<boost::shared_mutex> lock(_access); 

    // get exclusive access 
    boost::upgrade_to_unique_lock<boost::shared_mutex> uniqueLock(lock); 
    // now we have exclusive access 
} 
+0

Vì vậy, nhiều hơn một thread có thể giữ một upgrade_lock? – Amnon

+0

Nhiều hơn một chủ đề có thể * sử dụng * nó, nhưng chỉ một người có thể * giữ nó độc quyền * bất cứ lúc nào. Nhiều người đọc đồng thời là tốt, miễn là không có nhà văn hiện tại. Đó là những gì bạn muốn, tôi tưởng tượng? –

+1

Và làm cách nào để mở khóa? –

3

Bạn biết LightweightLock hoặc cùng một lúc LightweightLock_zip
thực hiện chính xác những gì bạn muốn. Tôi đã sử dụng nó một thời gian dài.

[EDIT] đây là nguồn:


///////////////////////////////////////////////////////////////////////////// 
// 
// Copyright (C) 1995-2002 Brad Wilson 
// 
// This material is provided "as is", with absolutely no warranty 
// expressed or implied. Any use is at your own risk. Permission to 
// use or copy this software for any purpose is hereby granted without 
// fee, provided the above notices are retained on all copies. 
// Permission to modify the code and to distribute modified code is 
// granted, provided the above notices are retained, and a notice that 
// the code was modified is included with the above copyright notice. 
// 
///////////////////////////////////////////////////////////////////////////// 
// 
// This lightweight lock class was adapted from samples and ideas that 
// were put across the ATL mailing list. It is a non-starving, kernel- 
// free lock that does not order writer requests. It is optimized for 
// use with resources that can take multiple simultaneous reads, 
// particularly when writing is only an occasional task. 
// 
// Multiple readers may acquire the lock without any interference with 
// one another. As soon as a writer requests the lock, additional 
// readers will spin. When the pre-writer readers have all given up 
// control of the lock, the writer will obtain it. After the writer 
// has rescinded control, the additional readers will gain access 
// to the locked resource. 
// 
// This class is very lightweight. It does not use any kernel objects. 
// It is designed for rapid access to resources without requiring 
// code to undergo process and ring changes. Because the "spin" 
// method for this lock is "Sleep(0)", it is a good idea to keep 
// the lock only long enough for short operations; otherwise, CPU 
// will be wasted spinning for the lock. You can change the spin 
// mechanism by #define'ing __LW_LOCK_SPIN before including this 
// header file. 
// 
// VERY VERY IMPORTANT: If you have a lock open with read access and 
// attempt to get write access as well, you will deadlock! Always 
// rescind your read access before requesting write access (and, 
// of course, don't rely on any read information across this). 
// 
// This lock works in a single process only. It cannot be used, as is, 
// for cross-process synchronization. To do that, you should convert 
// this lock to using a semaphore and mutex, or use shared memory to 
// avoid kernel objects. 
// 
// POTENTIAL FUTURE UPGRADES: 
// 
// You may consider writing a completely different "debug" version of 
// this class that sacrifices performance for safety, by catching 
// potential deadlock situations, potential "unlock from the wrong 
// thread" situations, etc. Also, of course, it's virtually mandatory 
// that you should consider testing on an SMP box. 
// 
/////////////////////////////////////////////////////////////////////////// 

#pragma once 

#ifndef _INC_CRTDBG 
#include 
#endif 

#ifndef _WINDOWS_ 
#include 
#endif 

#ifndef __LW_LOCK_SPIN 
#define __LW_LOCK_SPIN Sleep(0) 
#endif 


    class LightweightLock 
    { 
    // Interface 

    public: 
     // Constructor 

     LightweightLock() 
     { 
      m_ReaderCount = 0; 
      m_WriterCount = 0; 
     } 

     // Destructor 

     ~LightweightLock() 
     { 
      _ASSERTE(m_ReaderCount == 0); 
      _ASSERTE(m_WriterCount == 0); 
     } 

     // Reader lock acquisition and release 

     void LockForReading() 
     { 
      while(1) 
      { 
       // If there's a writer already, spin without unnecessarily 
       // interlocking the CPUs 

       if(m_WriterCount != 0) 
       { 
        __LW_LOCK_SPIN; 
        continue; 
       } 

       // Add to the readers list 

       InterlockedIncrement((long*) &m_ReaderCount); 

       // Check for writers again (we may have been pre-empted). If 
       // there are no writers writing or waiting, then we're done. 

       if(m_WriterCount == 0) 
        break; 

       // Remove from the readers list, spin, try again 

       InterlockedDecrement((long*) &m_ReaderCount); 
       __LW_LOCK_SPIN; 
      } 
     } 

     void UnlockForReading() 
     { 
      InterlockedDecrement((long*) &m_ReaderCount); 
     } 

     // Writer lock acquisition and release 

     void LockForWriting() 
     { 
      // See if we can become the writer (expensive, because it inter- 
      // locks the CPUs, so writing should be an infrequent process) 

      while(InterlockedExchange((long*) &m_WriterCount, 1) == 1) 
      { 
       __LW_LOCK_SPIN; 
      } 

      // Now we're the writer, but there may be outstanding readers. 
      // Spin until there aren't any more; new readers will wait now 
      // that we're the writer. 

      while(m_ReaderCount != 0) 
      { 
       __LW_LOCK_SPIN; 
      } 
     } 

     void UnlockForWriting() 
     { 
      m_WriterCount = 0; 
     } 

     long GetReaderCount() { return m_ReaderCount; }; 
     long GetWriterConut() { return m_WriterCount; }; 

    // Implementation 

    private: 
     long volatile m_ReaderCount; 
     long volatile m_WriterCount; 
    }; 


+2

Liên kết bị hỏng. Và nó có vẻ giống như một thư viện .net trong khi tôi đang tìm kiếm một giải pháp C++ (thêm thẻ). – Amnon

+0

Chỉ cần một lưu ý, mã trên là không công bằng và có những tình huống mà chủ đề sẽ không bao giờ truy cập và chờ đợi mãi mãi .. – ROAR

+0

Điều này không bao giờ xảy ra trong thời gian tôi đang sử dụng triển khai này. Đã bảy năm và mã chạy hơn 10 triệu lần mỗi ngày. Đơn giản và rất tốt. – lsalamon

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