多线程 - 同步机制
适当的使用多线程可以提高计算机资源利用效率,但是多线程是把双刃剑,使用的时候,切忌小心。容易造成数据错误,并且这样的问题并不是总是发生,很难检测出。
为了数据或者代码被多个线程同时访问或者修改而造成的问题,解决的一个i额方法是使用同步机制 。
1.Lock
Lock就像它长的那样,锁住一段代码,只允许当前的一个线程访问,别的线程恰好也要访问的时候,会被挡住,这样的时候 ,其实线程不是并发运行,而是顺序运行了,效果类似单线程。
例子:
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); }
浙公网安备 33010602011771号