简体   繁体   中英

thread-safe usage of std::map

I have an std::map<int, object*> which I need to access (read and write) from different threads. Of course, I can just use one Critical Section for both read and write, but it will have a huge impact on performance, because I have lots of find() calls (a few thousands per second) and much fewer writes (usually, one insert and one erase while creating and destroying thread).

Therefore, I need to use CriticalSection for writing and only check if another thread is doing write operation before reading. But how? I found solutions for C++11 and boost, but I am using Visual Studio 2008 (because of compatibility issues).

Can someone give me an example or explanation of how to do it? Thanks!

You can make a class that wraps your std::map and locks the write/read functions down with a mutex. Use a mutex structure as a member of this class and lock/unlock as appropriate per function.

Windows API has some mutex functions to register a mutex handle with the system. The handle acts as a way for Windows to recognize the mutex and check if it's waiting.

Here's a simple mutex class to get you started using some of the Windows API calls.

class MyMutex {

    private:
        HANDLE m_hMutex; 

    public:
        MyMutex()
        {
            m_hMutex = CreateMutex(NULL, FALSE, NULL);
        }

        ~MyMutex()
        {
            CloseHandle(m_hMutex);
        }

        void Lock()
        {
            BOOL test = WaitForSingleObject( m_hMutex, INFINITE );
        }

        void UnLock() 
        {
            ReleaseMutex( m_hMutex );
        }
};

What you are looking for is a multiple readers/single writer locking system. Unfortunately there is no built-in type for this (before C++14). If you can use boost, then you can go with boost's shared_mutex

If not, you're gonna have to do it yourself (read this other thread on SO). You can also use MS SRW lock, as stated by TC in his comment (see there ).

Now, suppose you have your class shared_mutex well defined and available. You just have to add one shared_mutex object as an attribute to the class you want to protect. I would advise you to keep completely locking-free methods in your class and add wrappers around them, like this:

class Whatever;
class MyClass {
    boost::shared_mutex mutex;
    Whatever find_unlocked() {
        whatever blob_blob;
        blob_blob = do_whatever_work_find_does();
        return blob_blob;
    }
    void write_smth_unlocked() {
        do_something_that_needs_writing_to_MyClass();
    }
public:
    Whatever find() {
        Whatever blob;
        mutex.lock_shared(); // Locks for reading (shared ownership on the mutex)
        blob = find_unlocked();
        mutex.unlock_shared();
        return blob;
    }
    void write_smth() {
        mutex.lock(); // Locks for writing (exclusive ownership on the mutex)
        write_smth_unlocked();
        mutex.unlock();
    }
};

Doing so will grant you the ability to reuse the operations you define in new features for the class while still being able to protect the whole new operations through the locking system.

In the end you would have two methods for any operation you define:

  • A private (or protected) method: operation_unlocked()
  • A public one: operation() which uses the shared_mutex .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM