2010-11-17 1 views
12

Я пытаюсь выполнить следующий код в сценарии многопоточного:Многократных читатели, одного сочинителя замки в НЧ

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

Повышение нить имеет shared_mutex класс, который был разработан для нескольких читателей, одиночных -автоматическая модель. В этом классе есть несколько вопросов о stackoverflow. Тем не менее, я не уверен, что это соответствует сценарию выше, где любой читатель может стать писателем. В документации говорится:

Концепция UpgradeLockable является уточнение концепции SharedLockable, что позволяет обновляемой собственности, а также долевой собственности и исключительное право собственности. Это расширения к/ однофакторной модели записи множественного считывания, представленной SharedLockable концепции: а сингл нити может иметь обновляемую собственность в то же время, как другие имеют общие собственности.

Из слова «одиночный» я подозреваю, что только один поток может содержать обновляемый замок. Остальные только держат общий замок, который не может быть обновлен до эксклюзивной блокировки.

Знаете ли вы, что boost::shared_lock полезен в этой ситуации (любой читатель может стать писателем), или если есть какой-либо другой способ достичь этого?

+0

Каково ваше определение «эксклюзивный доступ», если это не «только один поток может удерживать блокировку за раз»? или вы путаете «один поток за раз» с «одним конкретным потоком, выбранным заранее»? –

+0

@Pete: Я хочу, чтобы один поток (* не * выбран заранее) удерживал эксклюзивную блокировку. – Amnon

ответ

5

boost::shared_lock не поможет в этой ситуации (несколько читателей, которые могут стать писателями), так как только один поток может владеть обновляемой замок. Это подразумевается цитатой из документации в вопросе и просмотром кода (thread \ win32 \ shared_mutex.hpp). Если поток пытается получить обновляемый замок, а другой поток содержит один, он будет ждать другого потока.

Я решил использовать обычный замок для всех читателей/писателей, что в порядке, так как критический раздел короток.

15

Да, вы можете сделать именно то, что хотите, как показано в принятом ответе here. Вызов обновления до эксклюзивного доступа блокируется до тех пор, пока все читатели не будут выполнены.

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

Так что более одного потока может содержать upgrade_lock? – Amnon

+0

Более чем один поток может * использовать * его, но только один может * удерживать его исключительно * в любое время. Множественные параллельные считыватели хороши, если нет текущего автора. Думаю, это то, чего вы хотите? –

+1

И как мне его разблокировать? –

3

Вы знаете LightweightLock или же на LightweightLock_zip
делает именно то, что вы хотите. Я использовал его долгое время.

[EDIT] вот источник:


///////////////////////////////////////////////////////////////////////////// 
// 
// 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

Неработающие ссылки. И это звучит как библиотека .net, в то время как я ищу C++-решение (добавленный тег). – Amnon

+0

Просто примечание, над кодом несправедливо, и есть ситуации, когда потоки никогда не получат доступ и не будут ждать. – ROAR

+0

Это никогда не случалось в то время, когда я использую эту реализацию. Прошло семь лет, и код проработал более 10 миллионов раз в день. Просто и очень хорошо. – lsalamon