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