Блокировка самозагружающегося кэша - PullRequest
1 голос
/ 30 июня 2009

Я реализую простой кеш в C # и пытаюсь сделать его доступным из нескольких потоков. В базовом случае чтения это просто:

var cacheA = new Dictionary<int, MyObj>(); // Populated in constructor

public MyObj GetCachedObjA(int key)
{
    return cacheA[key];
}

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

Dictionary<int, MyObj> cacheB = null;

public MyObj GetCachedObjB(int key)
{
    if (cacheB == null)
    {
        PopulateCacheB();
    }
    return cacheB[key];
}

private void PopulateCacheB()
{
    cacheB = new Dictionary<int, MyObj>();
    foreach (MyObj item in databaseAccessor)
    {
        cacheB.Add(item.Key, item);
    }
}

Это не потокобезопасно, поскольку поток может получить доступ к GetCachedObjB после создания экземпляра cacheB, но до его полного заполнения, если другой поток находится в процессе его заполнения. Итак, каков наилучший способ выполнить блокировку для cacheB, чтобы кэш был потокобезопасным?

Ответы [ 6 ]

2 голосов
/ 30 июня 2009

Как это: http://devplanet.com/blogs/brianr/archive/2008/09/26/thread-safe-dictionary-in-net.aspx

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

1 голос
/ 30 июня 2009

Вы можете использовать оператор lock для простой безопасности потоков:

private Dictionary<int, MyObj> cacheB = null;
private readonly object cacheLockB = new object();

public MyObj GetCachedObjB(int key)
{
    lock (cacheLockB)
    {
        if (cacheB == null)
        {
            Dictionary<int, MyObj> temp = new Dictionary<int, MyObj>();
            foreach (MyObj item in databaseAccessor)
            {
                temp.Add(item.Key, item);
            }
            cacheB = temp;
        }
        return cacheB[key];
    }
}

Если вам нужно выжать больше производительности, чем позволяет lock, вы можете вместо этого использовать ReaderWriterLockSlim, что позволит нескольким потокам читать из словаря одновременно. Когда вам нужно заполнить или обновить словарь, вы можете просто обновить блокировку до режима записи.

1 голос
/ 30 июня 2009

Предполагая, что ваш словарь является потокобезопасным или доступен только для чтения после заполнения, вы можете заполнить его потокобезопасным способом, таким образом:

private void PopulateCacheB()
{    
    Dictionary<int, MyObj>() dictionary = new Dictionary<int, MyObj>();    
    foreach (MyObj item in databaseAccessor)    
    {        
        dictionary.Add(item.Key, item);    
    }
    cacheB = dictionary;
}

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

1 голос
/ 30 июня 2009

Завершение операции заполнения в мониторе. Таким образом, если другой поток попытается прочитать кэш во время его заполнения, то поток будет вынужден ждать, пока операция заполнения не завершится / с ошибками, прежде чем он сможет предпринять попытку чтения.

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

public MyObj GetCachedObjB(int key)
{
    try {
        Monitor.Enter(cacheB);

        if (cacheB == null)
        {
            PopulateCacheB();
        }
    } finally {
        Monitor.Exit(cacheB);
    }
    return cacheB[key];
}   

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

1 голос
/ 30 июня 2009

Вы можете использовать ReaderWriterLock .

0 голосов
/ 30 июня 2009

Вы можете использовать Enterprise Library Cache, который является поточно-ориентированным.

...