Чтение / запись с STL Map в многопоточной среде - PullRequest
1 голос
/ 01 октября 2010

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

Я думаю об этом:

string GetData (const int key)
{

    pthread_rwlock_rdlock(&rwlock); //read lock
    string result = "not found";
    my_map::const_iterator iter = m.find(key);
    if ( iter != m.end() )//found
    {
       result = iter->second;
    }
    else //missing
    {
        pthread_rwlock_wrlock(&rwlock); // write lock
        //fetch value from data base


        //if successful, add to the map
          m[key] = "missing data";
          result = "missing data";
     pthread_rwlock_unlock(&rwlock); // unlock write lock
    }
    pthread_rwlock_unlock(&rwlock); // unlock read lock
    return result;
}

Является ли эта функция поточно-ориентированной? Разве два или более потоков не могут стоять в очереди при блокировке записи и запрашивать один и тот же ключ из базы данных? Если да, как я могу избежать этого сценария?

Ответы [ 3 ]

7 голосов
/ 01 октября 2010

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

Результаты не определены, если вызывающий поток удерживает блокировку чтения-записи (будь то блокировку чтения или записи) во время вызова [to pthread_rwlock_wrlock].

Это решение также не является безопасным для исключений. Если во время удержания блокировки выдается исключение, блокировка не будет снята, и ваше приложение, несомненно, будет заблокировано. Вы должны использовать библиотеку потоков C ++ (Boost.Thread, OpenThreads, just :: thread или что-то подобное), которая предоставляет C ++-ориентированный дизайн, поддерживающий такие вещи, как scoped_lock (или lock_guard).

Что касается правильного алгоритма, вам нужно что-то вроде:

obtain read lock
attempt to find object
if object exists
    return object
else
    release read lock
    obtain write lock
    if object exists
        return object
    else
        insert object
        return object

[Если вы используете какой-то lock_guard, вам не нужно беспокоиться об освобождении удерживаемых блокировок при возврате]

1 голос
/ 01 октября 2010

Не правильно реализовано.Вы не можете взять блокировку записи, все еще удерживая блокировку чтения, опасаясь тупика.На странице руководства для pthread_rwlock_wrlock в моем Linux-окне:

Функция pthread_rwlock_wrlock () должна применить блокировку записи к блокировке чтения-записи, на которую ссылается rwlock.Вызывающий поток получает блокировку записи, если ни один другой поток (читатель или записывающий) не удерживает блокировку чтения-записи.В противном случае поток должен блокироваться, пока не сможет получить блокировку. Вызывающий поток может зайти в тупик, если во время вызова он удерживает блокировку чтения-записи (будь то блокировка чтения или записи).

Далее следует проверитьвозвращаемое значение вызовов ... например, существует ограничение на число одновременных считывателей, определяемое реализацией.

Существуют также обычные проблемы с безопасностью исключений ... рассмотрите защиту области видимости или попытку / отловблок.

0 голосов
/ 01 октября 2010

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

string GetData (const int key)
{

    pthread_rwlock_rdlock(&rwlock); //read lock
    string result = "not found";
    my_map::const_iterator iter = m.find(key);
    if ( iter != m.end() )//found
    {
        result = iter->second;
    }
    else //missing
    {
        // change from read mode to write mode
        pthread_rwlock_unlock(&rwlock); // unlock read lock
        pthread_rwlock_wrlock(&rwlock); // write lock

        // Try again
        iter = m.find(key);
        if (iter != m.end()) {
            result = iter->second;
        } else {
            //if successful, add to the map
            m[key] = "missing data";
            result = "missing data";
        }
    }
    pthread_rwlock_unlock(&rwlock); // unlock read/write lock
    return result;
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...