在每个项目基础上同步线程

时间:2016-04-25 11:22:39

标签: c# multithreading synchronization locking semaphore

虽然这个问题与MemoryCache类有关,但我可以想象使用DictionaryConcurrentDictionary.GetOrAdd同样需要valueFactory - lambda也是一个冗长的操作。

本质上我想在每个项目的基础上同步/锁定线程。我知道MemoryCache是线程安全的,但是,检查项目是否存在并在项目不存在时添加项目仍然需要同步。

请考虑以下示例代码:

public class MyCache
{
    private static readonly MemoryCache cache = new MemoryCache(Guid.NewGuid().ToString());

    public object Get(string id)
    {
        var cacheItem = cache.GetCachedItem(id);
        if (cacheItem != null) return cacheItem.Value;
        var item = this.CreateItem(id);
        cache.Add(id, item, new CacheItemPolicy
        {
            SlidingExpiration = TimeSpan.FromMinutes(20)
        });
        return item;
    }

    private object CreateItem(string id)
    {
        // Lengthy operation, f.e. querying database or even external API
        return whateverCreatedObject;
    }
}

如您所见,我们需要同步cache.GetCachedItemcache.Add。但由于CreateItem是一个冗长的操作(因此MemoryCache),我不想像这个代码那样锁定所有线程:

public object Get(string id)
{
    lock (cache)
    {
        var item = cache.GetCachedItem(id);
        if (item != null) return item.Value;
        cache.Add(id, this.CreateItem(id), new CacheItemPolicy
        {
            SlidingExpiration = TimeSpan.FromMinutes(20)
        });
    }
}

此外,没有锁定不是一个选项,因为我们可以有多个线程为CreateItem调用id

我可以做的是为每个Semaphore创建一个唯一的名为id,因此锁定按项目进行。但这将是一个系统资源杀手,因为我们不想在我们的系统上注册+ 100K命名信号量。

我确定我不是第一个需要这种同步的人,但我没有找到适合这种情况的任何问题/答案。

我的问题是,是否有人可以针对这个问题提出一种不同的,资源友好的方法?

更新

我发现this NamedReaderWriterLocker类起初看起来很有前途,但使用起来很危险,因为当两个线程进入{{1时,两个线程可能会为同一个名称获取不同的ReaderWriterLockSlim实例}} ConcurrentDictionary同时。也许我可以在valueFactory方法中使用一些额外的锁来使用这个实现。

1 个答案:

答案 0 :(得分:2)

由于您的密钥是字符串,因此您可以锁定string.Intern(id)

MSDN文档:System.String.Intern

lock (string.Intern(id))
{
    var item = cache.GetCachedItem(id);
    if (item != null)
    {
        return item.Value;
    }

    cache.Add(id, this.CreateItem(id), new CacheItemPolicy
    {
        SlidingExpiration = TimeSpan.FromMinutes(20)
    });

   return /* some value, this line was absent in the original code. */;
}