C# 条件线程锁定 脚本
我想下载资源。我不希望资源被多次下载。如果线程a下载资源C# 条件线程锁定 脚本,c#,multithreading,thread-safety,C#,Multithreading,Thread Safety,我想下载资源。我不希望资源被多次下载。如果线程a下载资源1,它应该被缓存;如果线程b同时尝试下载资源1,它应该等待并使用缓存的资源1。如果线程c想要下载资源2,它不应该受到线程a和b的影响 企图 我已尝试实现以下场景: using System; using System.Collections.Generic; using System.Threading; namespace ConsoleApplication1 { class ConditionalThreadLockingP
1
,它应该被缓存;如果线程b同时尝试下载资源1
,它应该等待并使用缓存的资源1
。如果线程c想要下载资源2
,它不应该受到线程a和b的影响
企图
我已尝试实现以下场景:
using System;
using System.Collections.Generic;
using System.Threading;
namespace ConsoleApplication1
{
class ConditionalThreadLockingProgram
{
private static readonly object _lockObject = new object();
private static readonly Dictionary<int, string> Locks =
new Dictionary<int, string>();
private static readonly Dictionary<int, string> Resources =
new Dictionary<int, string>();
public static string GetLock(int resourceId)
{
lock (_lockObject)
{
if (Locks.ContainsKey(resourceId))
{
return Locks[resourceId];
}
return Locks[resourceId] = string.Format(
"Lock #{0}",
resourceId
);
}
}
public static void FetchResource(object resourceIdObject)
{
var resourceId = (int)resourceIdObject;
var currentLock = GetLock(resourceId);
lock (currentLock)
{
if (Resources.ContainsKey(resourceId))
{
Console.WriteLine(
"Thread {0} got cached: {1}",
Thread.CurrentThread.Name,
Resources[resourceId]
);
return;
}
Thread.Sleep(2000);
Console.WriteLine(
"Thread {0} downloaded: {1}",
Thread.CurrentThread.Name,
Resources[resourceId] = string.Format(
"Resource #{0}",
resourceId
)
);
}
}
static void Main(string[] args)
{
new Thread(FetchResource) { Name = "a" }.Start(1);
new Thread(FetchResource) { Name = "b" }.Start(1);
new Thread(FetchResource) { Name = "c" }.Start(2);
Console.ReadLine();
}
}
}
使用系统;
使用System.Collections.Generic;
使用系统线程;
命名空间控制台应用程序1
{
类条件线程锁定程序
{
私有静态只读对象_lockObject=新对象();
专用静态只读字典锁=
新字典();
私有静态只读字典资源=
新字典();
公共静态字符串GetLock(int-resourceId)
{
锁定(锁定对象)
{
if(Locks.ContainsKey(resourceId))
{
返回锁[resourceId];
}
返回锁[resourceId]=string.Format(
“锁#{0}”,
足智多谋的
);
}
}
公共静态资源(对象资源对象)
{
var resourceId=(int)resourceIdObject;
var currentLock=GetLock(resourceId);
锁(当前锁)
{
if(Resources.ContainsKey(resourceId))
{
控制台写入线(
“线程{0}已缓存:{1}”,
Thread.CurrentThread.Name,
资源[资源ID]
);
返回;
}
《睡眠》(2000年);
控制台写入线(
“线程{0}已下载:{1}”,
Thread.CurrentThread.Name,
Resources[resourceId]=string.Format(
“资源#{0}”,
足智多谋的
)
);
}
}
静态void Main(字符串[]参数)
{
新线程(FetchResource){Name=“a”}.Start(1);
新线程(FetchResource){Name=“b”}.Start(1);
新线程(FetchResource){Name=“c”}.Start(2);
Console.ReadLine();
}
}
}
问题:
它有用吗?有什么问题吗?C#现在包含和-添加对MemoryCache的System.Runtime.Caching的引用
下面是我要做的-不需要额外的锁定,惰性实现负责竞争条件
/// <summary>
/// Summary description for ResourceFactory
/// </summary>
public static class ResourceFactory
{
private const string _cacheKeyFormat = "AppResource[{0}]";
private static readonly ObjectCache _cache = MemoryCache.Default;
private static readonly CacheItemPolicy _policy = new CacheItemPolicy()
{
SlidingExpiration = TimeSpan.FromMinutes(Int32.Parse(ConfigurationManager.AppSettings["AppResourceTimeout"] ?? "20")),
RemovedCallback = new CacheEntryRemovedCallback(AppResourceRemovedCallback)
};
private static void AppResourceRemovedCallback(CacheEntryRemovedArguments args)
{
// item was removed from cache
}
#region Extensions to make ObjectCache work with Lazy
public static TValue GetOrAdd<TKey, TValue>(this ObjectCache @this, TKey key, Func<TKey, TValue> valueFactory, CacheItemPolicy policy)
{
Lazy<TValue> lazy = new Lazy<TValue>(() => valueFactory(key), true);
return ((Lazy<TValue>)@this.AddOrGetExisting(key.ToString(), lazy, policy) ?? lazy).Value;
}
public static TValue GetOrAdd<TKey, TParam1, TValue>(this ObjectCache @this, TKey key, TParam1 param1, Func<TKey, TParam1, TValue> valueFactory, CacheItemPolicy policy)
{
Lazy<TValue> lazy = new Lazy<TValue>(() => valueFactory(key, param1), true);
return ((Lazy<TValue>)@this.AddOrGetExisting(key.ToString(), lazy, policy) ?? lazy).Value;
}
#endregion
public static AppResourceEntity GetResourceById(int resourceId)
{
#region sanity checks
if (resourceId < 0) throw new ArgumentException("Invalid parameter", "resourceId");
#endregion
string key = string.Format(_cacheKeyFormat, resourceId);
AppResourceEntity resource = _cache.GetOrAdd(
key,
resourceId,
(k, r) =>
{
return AppResourceDataLayer.GetResourceById(r);
},
_policy
);
return resource;
}
}
//
///ResourceFactory的摘要说明
///
公共静态类资源工厂
{
私有常量字符串_cacheKeyFormat=“AppResource[{0}]”;
私有静态只读对象缓存_cache=MemoryCache.Default;
私有静态只读CacheItemPolicy _policy=new CacheItemPolicy()
{
SlidingExpiration=TimeSpan.FromMinutes(Int32.Parse(ConfigurationManager.AppSettings[“AppResourceTimeout”]??“20”),
RemovedCallback=新建CacheEntryRemovedCallback(AppResourceRemovedCallback)
};
私有静态void AppResourceRemovedCallback(CacheEntryRemovedArguments参数)
{
//项已从缓存中删除
}
#使ObjectCache与Lazy一起工作的区域扩展
公共静态TValue GetOrAdd(this ObjectCache@this,TKey key,Func valueFactory,CacheItemPolicy策略)
{
Lazy Lazy=新的Lazy(()=>valueFactory(key),true);
return((Lazy)@this.AddOrGetExisting(key.ToString(),Lazy,policy)??Lazy).Value;
}
公共静态TValue GetOrAdd(this ObjectCache@this,TKey key,TParam1 param1,Func valueFactory,CacheItemPolicy策略)
{
Lazy Lazy=new Lazy(()=>valueFactory(key,param1),true);
return((Lazy)@this.AddOrGetExisting(key.ToString(),Lazy,policy)??Lazy).Value;
}
#端区
公共静态AppResourceEntity GetResourceById(int resourceId)
{
#区域健全性检查
如果(resourceId<0)抛出新的ArgumentException(“无效参数”、“resourceId”);
#端区
string key=string.Format(\u cacheKeyFormat,resourceId);
AppResourceEntity resource=\u cache.GetOrAdd(
钥匙
智囊团,
(k,r)=>
{
返回AppResourceDataLayer.GetResourceById(r);
},
_政策
);
返回资源;
}
}
是的,我认为它有效。但我对多线程非常陌生。我可能会建议使用字典的SyncRoot,而不是创建自己的锁。GetLock将使用(((IDictionary)Locks.SyncRoot)和FetchResources将使用(((IDictionary)Resources.SyncRoot)如果您正在寻找有关工作代码的反馈,将您的问题发布到可能更合适。moncadad:谢谢您的建议。有人说使用SyncRoot
属性可能会有危险:dgvid:谢谢你告诉我codereview.stackexchange.com。以前从未使用过那个网站。我觉得这个问题是关于在同一个方法中使用不同锁对象的概念。我觉得这是一个一般性的问题,需要一个例子来澄清。