Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/327.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/multithreading/4.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 条件线程锁定 脚本_C#_Multithreading_Thread Safety - Fatal编程技术网

C# 条件线程锁定 脚本

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

我想下载资源。我不希望资源被多次下载。如果线程a下载资源
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。以前从未使用过那个网站。我觉得这个问题是关于在同一个方法中使用不同锁对象的概念。我觉得这是一个一般性的问题,需要一个例子来澄清。