首先要做的事情。这是支持团队提供的一些异常信息。我知道它发生的行和代码。它发生在通过缓存获取的字典的FirstOrDefault调用中。
1) Exception Information
*********************************************
Exception Type: System.InvalidOperationException
Message: Collection was modified; enumeration operation may not execute.
Data: System.Collections.ListDictionaryInternal
现在我想模拟问题,我可以在一个简单的ASP.net应用程序中完成。
我的页面有2个按钮 - Button_Process 和 Button_Add
背后的代码如下:
public partial class _Default : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
if (!IsPostBack)
{
var data = Cache["key"];
if (data == null)
{
var dict = new Dictionary<int, string>();
for (int i = 0; i < 10; i++)
{
dict.Add(i, "i");
}
Cache["key"] = dict;
}
}
}
protected void ButtonProcess_Click(object sender, EventArgs e)
{
var data = Cache["key"] as Dictionary<int, string>;
if (data != null)
{
foreach (var d in data.Values) //In actual code there is FirstOrDefault here
{
Thread.Sleep(1000);
if (d.Contains("5"))
{
//some operation
}
}
}
}
protected void Button2_Click(object sender, EventArgs e)
{
var data = Cache["key"] as Dictionary<int, string>;
if (data != null)
{
data.Add(new Random().Next(), "101");
Cache["key"] = data;
}
}
}
现在假设有2个请求:
请求1 - 有人点击了button_Process,并且正在对缓存对象进行一些操作 请求2 - 有人点击button_Add并且第一个人获得了异常 - 收集修改后的等等等等
我理解这个问题 - 它正在发生,因为我们正在访问相同的内存。我脑子里有两个解决方案:
1。我使用for循环而不是每个(在实际代码中替换FirstOrDefault) - 我不知道在进行更改后此操作的效率如何。 - 我不会从缓存中删除任何项目,所以我在想这个解决方案
2。我对缓存对象或其他东西进行了一些锁定 - 但我确切地知道我应该在哪里以及如何锁定这个对象。
请帮帮我。我无法找到有效的解决方案。处理此类情况的最佳方法是什么?
答案 0 :(得分:3)
这是因为您直接使用对象,位于缓存中。好的做法是,避免这些异常和其他奇怪的行为(当您意外修改缓存对象时)正在使用缓存数据的 copy 。有几种方法可以实现它,例如执行 clone 或某种深层复制。我更喜欢将对象保存在高速缓存中序列化(您喜欢的任何类型 - json / xml / binary或w / e else),因为(de)序列化会对您的对象进行深层复制。以下小代码片段将澄清事情:
public static class CacheManager
{
private static readonly Cache MyCache = HttpRuntime.Cache;
public static void Put<T>(T data, string key)
{
MyCache.Insert(key, Serialize(data));
}
public static T Get<T>(string key)
{
var data = MyCache.Get(key) as string;
if (data != null)
return Deserialize<T>(data);
return default(T);
}
private static string Serialize(object data)
{
//this is Newtonsoft.Json serializer, but you can use the one you like
return JsonConvert.SerializeObject(data);
}
private static T Deserialize<T>(string data)
{
return JsonConvert.DeserializeObject<T>(data);
}
}
用法:
var myObj = new Dictionary<int, int>();
CacheManager.Put(myObj, "myObj");
//...
var anotherObj = CacheManager.Get<Dictionary<int, int>>("myObj");
答案 1 :(得分:1)
检查.NET 3.5的任务并行库。它具有ConcurrentStctions,例如ConcurrentStack,ConcurentQueue和ConcurrentDictionary。
答案 2 :(得分:1)
问题是缓存对象对于appdomain是全局的,并且存储的数据在所有请求之间共享。 解决此问题的唯一方法是在您想要访问集合时激活锁定,然后释放锁定(https://msdn.microsoft.com/en-us/library/vstudio/c5kehkcz%28v=vs.100%29.aspx)。 (对不起,我的英文不好)