C#线程同步

多个线程在同时访问一个数据时,需要对访问进行控制,避免损坏数据。

class Program
{
    static void Main(){
			int sum = 0;

            for (int i = 1; i <= 100; i++)
            {
                ThreadPool.QueueUserWorkItem(value =>
                {
                        sum = sum+ (int)value;
                },i);
            }
            Thread.Sleep(1000);
            Console.WriteLine(sum);
    }
 }

以上代码通过多线程求和。我们知道,1加到100是5050,运行上面的程序,结果可能是4616。

计算机怎么会出错呢?

以下展示了多线程情况可能的执行步骤

1.线程1:i=1,sum=0,代码执行到了语句sum+=sum+ (int)value,已经计算完sum+(int)value,还未将结果赋2值到sum,线程1暂停,调度到线程2。这时sum=0.

2.线程2:i=2,sum=0,代码执行到语句sum+=(int)value,执行完成,此时sum=2,线程2结束,调度到线程1。

3.线程1:赋值1到变了sum。线程1结束。此时sum=1。

这个时候,计算就已经错误了,第3步覆盖了第2步的结果。

由于多线程在执行时会有难以预料的打断,因此在访问同一个变量时,需要有一个机制,保证其他线程不能访问。

锁限定了线程对某一个资源的使用权。

在C#中,可以使用lock语句来实现对资源的独占访问。

lock(obj)
{
    //在此处访问独占资源
}

在内部,lock语句是对Monitor类的调用。

try
{
    Monitor.Enter(obj);
      //在此处访问独占资源
}
finally
{
    Monitor.Exit(obj);
}

注意:锁对象不能是值类型,不能是应用程序域中立的类型,如string,Type。建议使用new object()作为锁。

Monitor支持重复占用锁,即嵌套lock语句可以lock同一obj

class Program
{
    static object syncObj = new object();

    static void Main(){
			int sum = 0;

            for (int i = 1; i <= 100; i++)
            {
                ThreadPool.QueueUserWorkItem(value =>
                {
                    	lock(syncObj)
                        {
                             sum = sum+ (int)value;
                        }
                },i);
            }
            Thread.Sleep(1000);
            Console.WriteLine(sum);
    }
 }

加了锁之后的结果是5050。