在多线程编程中,很多地方都会碰到同时操作一个全局变量的问题,我们在操作过程中应该怎么避免发生混乱呢?
分下面几个方面讨论:
从变量的特点上分,可以分为简单对象的安全问题和集合对象的安全问题。简单对象只要使用lock关键字就可以解决问题,这里主要讨论集合对象的安全问题。
集合对象一般我们使用较多的有键值对的集合,如:字典,哈希表等,一般都会有key ---value对。
这里举个最简单的例子:我们在很多地方可以看到操作webCache缓存的代码,如下;
当你网站的同时访问量够大,够多的时候,而第5行的运行需要一些时间的时候,可能发生,应用程序刚启动的时候,对于同一个key,有上百个人会同时来运行到第3行,此时上百个人判断出key的结果是null,此时全部一起去运行第5行(事实上对于同一个key,我们要让它只运行一次第5行)。
这个过程就是个很浪费效率的过程。
此时有人可能会问:“那我们在加载数据的时候锁不就得了”,看下面代码就是加锁的代码
服务器刚启动的时候,当n个人来运行的时候,无论来的是哪个key,都会导致阻赛等待,如果同时有3000人来访问,保证你的服务器挂机。
那么我们怎么解决这个问题呢。
我们可以..... 待续.....待续
还没写完呢,就看到赵兄在评论了。不错,赵兄的方法正解。要解决问题的方法还是比较简单的,主要问题是很多兄弟一下次眼昏没有注意到这个问题。
不过从完整的解决上来说,应该是新建一个字典,字典中key就是我们外面的key,字典中的value是我们的lockhelp,在读取之初,new一个lockhelp到字典中,然后加锁开始读取数据,而当字典中有这个key的lockhelp时,直接拿出来lock住。
==========================================================================
这种方法:赵兄在3楼有所点评,赵兄的意思是如果同时操作的key过多,会产生大批量的锁。这确实是个问题。
如果我们要做如上方式缓存,一般情况下是对所有传进来的Key的相同率较高才会使用。如果key传进来基本上不一样,那么用上面这种缓存策略意义不是很大。如:论坛帖子,再热门的论坛我看也就最新的几篇,最热门的几篇,最精华的几篇的id传进来概率大。所以说,上面的缓存策略应用环境是针对key相同匹配率较高的环境,此时lockhelp不会出现超大的情况,毕竟我提出的问题是发生在 应用程序刚刚启动的时候,如果进入正常运行了是不会有这个问题的。
而针对key情况超大的时候,我们不是很必要用上面的缓存方法。key超大,说明缓存内容的差异性超大,每个人来访问的内容都会不一样。
==========================================================================
对于6楼提出的问题,我想可以用简单的代码示范一下
代码一:
===========================================================================
随着我们应用的越来越复杂,很多不同的情况下对线程安全都要做不同的处理。
如:当我们碰到对集合的更新是整个集合更新,而不是一个key一个key更新,更新频率又比较高,每次都是整个集合更新,这个时候我们又该如何保证我们的线程安全。
此时我们一般是采用在轮询过程中,先从数据库中提取新的集合到临时集合中,然后赋值给使用中的集合。
分下面几个方面讨论:
从变量的特点上分,可以分为简单对象的安全问题和集合对象的安全问题。简单对象只要使用lock关键字就可以解决问题,这里主要讨论集合对象的安全问题。
集合对象一般我们使用较多的有键值对的集合,如:字典,哈希表等,一般都会有key ---value对。
这里举个最简单的例子:我们在很多地方可以看到操作webCache缓存的代码,如下;
1
public MyObject Get(String key)
2
{
3
MyObject o;
4
if(CacheHelper[key]==null){
5
o=从数据库或者其他数据源获取o对象;
6
CacheHelper.Insert(key,o,..........
7
}
8
return o;
9
}
上面代码貌似比较好的解决了缓存问题,但是确存在着效率浪费的问题。(假设上面的缓存内容是某个论坛的帖子)![](/Images/OutliningIndicators/None.gif)
2
![](/Images/OutliningIndicators/ExpandedBlockStart.gif)
3
![](/Images/OutliningIndicators/InBlock.gif)
4
![](/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
5
![](/Images/OutliningIndicators/InBlock.gif)
6
![](/Images/OutliningIndicators/InBlock.gif)
7
![](/Images/OutliningIndicators/ExpandedSubBlockEnd.gif)
8
![](/Images/OutliningIndicators/InBlock.gif)
9
![](/Images/OutliningIndicators/ExpandedBlockEnd.gif)
当你网站的同时访问量够大,够多的时候,而第5行的运行需要一些时间的时候,可能发生,应用程序刚启动的时候,对于同一个key,有上百个人会同时来运行到第3行,此时上百个人判断出key的结果是null,此时全部一起去运行第5行(事实上对于同一个key,我们要让它只运行一次第5行)。
这个过程就是个很浪费效率的过程。
此时有人可能会问:“那我们在加载数据的时候锁不就得了”,看下面代码就是加锁的代码
1
private static object lockhelp=new object();
2![](/Images/OutliningIndicators/None.gif)
3
public MyObject Get(String key)
4
{
5
MyObject o;
6
if(CacheHelper[key]==null){
7
lock(lockhelp){
8
if(CacheHelper[key]==null){
9
o=从数据库或者其他数据源获取o对象;
10
CacheHelper.Insert(key,o,.
11
}
12
}
13
}
14
return o;
15
}
此时确实是解决了一个对象只被加载一次,不过确发生了更糟糕的问题。![](/Images/OutliningIndicators/None.gif)
2
![](/Images/OutliningIndicators/None.gif)
3
![](/Images/OutliningIndicators/None.gif)
4
![](/Images/OutliningIndicators/ExpandedBlockStart.gif)
5
![](/Images/OutliningIndicators/InBlock.gif)
6
![](/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
7
![](/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
8
![](/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
9
![](/Images/OutliningIndicators/InBlock.gif)
10
![](/Images/OutliningIndicators/InBlock.gif)
11
![](/Images/OutliningIndicators/ExpandedSubBlockEnd.gif)
12
![](/Images/OutliningIndicators/ExpandedSubBlockEnd.gif)
13
![](/Images/OutliningIndicators/ExpandedSubBlockEnd.gif)
14
![](/Images/OutliningIndicators/InBlock.gif)
15
![](/Images/OutliningIndicators/ExpandedBlockEnd.gif)
服务器刚启动的时候,当n个人来运行的时候,无论来的是哪个key,都会导致阻赛等待,如果同时有3000人来访问,保证你的服务器挂机。
那么我们怎么解决这个问题呢。
我们可以..... 待续.....待续
还没写完呢,就看到赵兄在评论了。不错,赵兄的方法正解。要解决问题的方法还是比较简单的,主要问题是很多兄弟一下次眼昏没有注意到这个问题。
不过从完整的解决上来说,应该是新建一个字典,字典中key就是我们外面的key,字典中的value是我们的lockhelp,在读取之初,new一个lockhelp到字典中,然后加锁开始读取数据,而当字典中有这个key的lockhelp时,直接拿出来lock住。
==========================================================================
这种方法:赵兄在3楼有所点评,赵兄的意思是如果同时操作的key过多,会产生大批量的锁。这确实是个问题。
如果我们要做如上方式缓存,一般情况下是对所有传进来的Key的相同率较高才会使用。如果key传进来基本上不一样,那么用上面这种缓存策略意义不是很大。如:论坛帖子,再热门的论坛我看也就最新的几篇,最热门的几篇,最精华的几篇的id传进来概率大。所以说,上面的缓存策略应用环境是针对key相同匹配率较高的环境,此时lockhelp不会出现超大的情况,毕竟我提出的问题是发生在 应用程序刚刚启动的时候,如果进入正常运行了是不会有这个问题的。
而针对key情况超大的时候,我们不是很必要用上面的缓存方法。key超大,说明缓存内容的差异性超大,每个人来访问的内容都会不一样。
==========================================================================
对于6楼提出的问题,我想可以用简单的代码示范一下
代码一:
1
private static Object o=new Object();
2![](/Images/OutliningIndicators/None.gif)
3
public void MyF(){
4
lock(o){
5
//dosamething
6
}
7
}
代码二:![](/Images/OutliningIndicators/None.gif)
2
![](/Images/OutliningIndicators/None.gif)
3
![](/Images/OutliningIndicators/ExpandedBlockStart.gif)
4
![](/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
5
![](/Images/OutliningIndicators/InBlock.gif)
6
![](/Images/OutliningIndicators/ExpandedSubBlockEnd.gif)
7
![](/Images/OutliningIndicators/ExpandedBlockEnd.gif)
1
public void MyF(){
2
Object o=new Object();
3
lock(o){
4
//dosamething
5
}
6
}
上面两个代码,代码一 就是n个人进来会阻塞,你认为代码二 n个人进来也会阻塞吗?你可以代码调式一下。![](/Images/OutliningIndicators/ExpandedBlockStart.gif)
2
![](/Images/OutliningIndicators/InBlock.gif)
3
![](/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
4
![](/Images/OutliningIndicators/InBlock.gif)
5
![](/Images/OutliningIndicators/ExpandedSubBlockEnd.gif)
6
![](/Images/OutliningIndicators/ExpandedBlockEnd.gif)
===========================================================================
随着我们应用的越来越复杂,很多不同的情况下对线程安全都要做不同的处理。
如:当我们碰到对集合的更新是整个集合更新,而不是一个key一个key更新,更新频率又比较高,每次都是整个集合更新,这个时候我们又该如何保证我们的线程安全。
此时我们一般是采用在轮询过程中,先从数据库中提取新的集合到临时集合中,然后赋值给使用中的集合。