多线程 - 同步机制

适当的使用多线程可以提高计算机资源利用效率,但是多线程是把双刃剑,使用的时候,切忌小心。容易造成数据错误,并且这样的问题并不是总是发生,很难检测出。

为了数据或者代码被多个线程同时访问或者修改而造成的问题,解决的一个i额方法是使用同步机制 。

1.Lock

Lock就像它长的那样,锁住一段代码,只允许当前的一个线程访问,别的线程恰好也要访问的时候,会被挡住,这样的时候 ,其实线程不是并发运行,而是顺序运行了,效果类似单线程。

例子:

 1 using System;
 2 using System.Collections.Generic;
 3 using System.Linq;
 4 using System.Text;
 5 using System.Threading;
 6 
 7 namespace ConsoleApplication1
 8 {
 9     class Program
10     {
11         static AutoResetEvent autoEvent;
12         static void Main(string[] args)
13         {
14  List<Thread> threads = new List<Thread>();
15             NaturalSource ns = new NaturalSource();
16             for (int i = 0; i < 10; i++)
17             {
18                 Thread t = new Thread(new ThreadStart(ns.GetFromEarth));
19                 threads.Add(t);
20             }
21             foreach (Thread th in threads)
22             {
23                 th.Start();
24             }
25      }
26 
27 public class NaturalSource
28     {
29         private Object myLock = new Object();
30         private List<string> _source;
31 
32         public NaturalSource()
33         {
34             _source = new List<string> { "Oil""Gass""Food""Water" };
35         }
36 
37         private string GetSomething()
38         {
39             lock (myLock)
40             {
41                 if (_source.Count > 0)
42                 {
43                     string resource = _source[0];
44                     Console.WriteLine(string.Format("{0} get {1} from the Earth", Thread.CurrentThread.GetHashCode(), resource));
45                     _source.Remove(resource);
46                     return resource;
47                 }
48                 else
49                 {
50                     Console.WriteLine("The nature source is used up!please save the earth!");
51                     return string.Empty;
52                 }
53             }
54         }
55 
56         public void GetFromEarth()
57         {
58             GetSomething();
59         }
60     }

61 } 

 我们故意在Main中调用十次,向Earth索要资源,但是NaturalSource只有10个,所以只有四个Thread能抢到资源,剩下的六个没有的;

但是如果不加Lock的话,我们可能发现有六个Therad抢到了资源,而这个是不可能的,因为一共只有四个,另外的两个肯定是数据不同造成的错误。

 

Lock很好,也很简单,但是用Lock要特别小心 ,认真计划。

一,用lock后,并发的优势就在执行Lock住的那块代码里面没有了,因为那里只能是一个一个的执行。

二,Lock用的不恰当,可能造成deadlock,比如,在一个锁释放前,又加了另外的锁。。。。

三,就是Lock(),括号中的,msdn上面建议不要锁定三种类型lock (this)、lock (typeof (MyType)) 和 lock ("a") 

this指的是锁定当前class的实例 ,但是可能一个class有多个instance,并且当锁定this,其他的method都不好用了,这样降低了效率

typeof(mytype)就更 广泛了,

“a”,lock因为只能锁定ref类型的对象,string也是ref型的不错,但是比较特殊,string a=“a”; a=“b"; 其实就是有两个string对象了 经常锁定失败

此外,也不要这样

object locker;

lock(locker)


locker=..... 

} 

一般最好不要操作locker,

locker一般建议使用 private static 并且没有代码对他进行操作的对象。

 

另:

lock住的方法里面,最好不要使用递归的方法。如下

 public void TestMethod()
    {
      lock (m_lockObject)
      {
	if(condition)
       TestMethod();
       //break condition
      }

    } 

因为TestMethod第一次调用的时候 ,会锁住m_lockObject,不给别的Thread执行这个statement,如果condition成立的时候,再次执行TestMethod,它又去申请锁住m_lockObject,尽管当前的Thread就是它自己,可是它就是去申请锁定,但是m_lockObject已经是锁定的状态,于是下面递归调用的就在等待,造成自己堵死了自己,造成死锁

 

2.与lock相似的机制

其他起到lock作用的有 Monitor, Mutex

Monitor通过两个方法 enter 和exit方法进行锁定和释放,起到作用和lock一样,所以推荐使用lock,更简单。

 

System.Object obj = (System.Object)x;
System.Threading.Monitor.Enter(obj);
try
{
    DoSomething();
}
finally
{
    System.Threading.Monitor.Exit(obj);
}
 

 

 

 

 

 

posted on 2011-12-28 20:30  鹤翔九天  阅读(182)  评论(0)    收藏  举报

导航