在多线程编程中,很多地方都会碰到同时操作一个全局变量的问题,我们在操作过程中应该怎么避免发生混乱呢?

分下面几个方面讨论:

从变量的特点上分,可以分为简单对象的安全问题和集合对象的安全问题。简单对象只要使用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}
上面代码貌似比较好的解决了缓存问题,但是确存在着效率浪费的问题。(假设上面的缓存内容是某个论坛的帖子)

当你网站的同时访问量够大,够多的时候,而第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}
此时确实是解决了一个对象只被加载一次,不过确发生了更糟糕的问题。

服务器刚启动的时候,当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}
代码二:
1public void MyF(){
2     Object o=new Object();
3     lock(o){
4       //dosamething
5     }

6}
上面两个代码,代码一 就是n个人进来会阻塞,你认为代码二 n个人进来也会阻塞吗?你可以代码调式一下。
===========================================================================

随着我们应用的越来越复杂,很多不同的情况下对线程安全都要做不同的处理。

如:当我们碰到对集合的更新是整个集合更新,而不是一个key一个key更新,更新频率又比较高,每次都是整个集合更新,这个时候我们又该如何保证我们的线程安全。

此时我们一般是采用在轮询过程中,先从数据库中提取新的集合到临时集合中,然后赋值给使用中的集合。

posted on 2008-01-25 23:15  狂图  阅读(6549)  评论(7编辑  收藏  举报