简单的同步

简单的同步

前言

好几天没写写C#的代码了,突发奇想,还是写写吧,别手生了.还是继续写点关于线程的小东西吧,这次说说线程的同步吧.

看下面这一段代码

class ThreadTest
    {
        static int _val1 = 1, _val2 = 1;
        internal static void Go()
        {
            if (_val2!=0)
            {
                Console.WriteLine(_val1/_val2);
            }
            _val2 = 0;
        }
}
 

分析:乍一看,没问题啊,但是我要告诉你,这段代码是费线程安全的,假设两个线程A和B,A和B都执行到了Go()方法的if判断中,假设_val2=1.所以两个线程都通过了了if判断,线程A执行了CW语句,然后退出了if语句,执行_val2=0,此时val2=0,但是此时线程B才刚刚执行到CW方法,而此时_val2=0.所以你有可能会得到一个divide by zero的异常.

so,为了保证线程安全,我们可以使用lock关键字,例如:

    class ThreadTest
    {
        static readonly object _locker = new object();
        static int _val1 = 1, _val2 = 1;
        internal static void Go()
        {
            lock (_locker)
            {
                if (_val2 != 0)
                {
                    Console.WriteLine(_val1 / _val2);
                }
                _val2 = 0;
            }

        }
    }
 

分析:此时两个线程A和B都只能有一个可以获得_locker锁,所以只能有一个线程来执行lock块的代码.

C#的lock关键字实际上是Moitor.Enter和Monitor.Exit的缩写.例如上面所写的代码和下面的等价:

Monitor.Enter(_locker);
            try
            {
                if (_val2 != 0)
                {
                    Console.WriteLine(_val1 / _val2);
                }
                _val2 = 0;
            }
            finally { Monitor.Exit(_locker); }

注意,如果在调用Monitor.Exit之前没有调用Monitor.Enter,则会抛出一个异常.

还有,在Monitor.Enter和try方法之间可能会抛出异常.例如在线程上调用Abort,或者是OutOfMemoryException.

为了解决这个问题,CLR 4.0提供了Monitor.Enter的重载,增加了lockTaken字段,当Monitor.Enter成果获取锁之后,lockTaken就是True,否则就为false.

我们同样可以将上面的代码写成下面这样:

    class ThreadTest
    {
        static readonly object _locker = new object();
        static int _val1 = 1, _val2 = 1;
        internal static void Go()
        {

            bool lockTaken = false;
            try
            {
                Monitor.Enter(_locker,ref lockTaken);
                if (_val2 != 0)
                {
                    Console.WriteLine(_val1 / _val2);
                }
                _val2 = 0;
            }
            finally
            {
                if (lockTaken)
                {
                    Monitor.Exit(_locker);
                }

            }

        }
    }
 

Monitor也提供了TryEnter方法,并且可以传递一个超时时间.如果方法返回true,则代表获取了锁,否则为false.

选择同步对象

Monitor.Enter方法的参数是一个object类型,所以任何对象都可以是同步的,考虑下面的代码:
int i=5;	lock(i){}	//锁定值类型
lock(this){}	//锁定this对象
lock(typeof(Product)){}//锁定type对象
string str=”ddd”;	lock(str){}	//锁定字符串

锁定值类型会将值类型进行装箱,所以Monitor.Enter进入的是一个对象,但是Monitor.Exit()退出的是另一个不同的对象.

锁定this和type对象,会导致无法控制锁的逻辑,并且它很难保证不死锁和频繁的阻塞,在相同进程中锁定type对象会穿越应用程序域.

由于字符串驻留机制,所以也不要锁定string.

嵌套锁

同一个线程可以多次锁定同一对象.例如

lock(locker)
    lock(locker)
        lock(locker)
        {
            // do something
     }
 

或者是

Monitor.Enter(locker);
Monitor.Enter(locker);
Monitor.Enter(locker);
//code
Monitor.Exit(locker);
Monitor.Exit(locker);
Monitor.Exit(locker);

当一个线程使用一个锁调用另一个方法的时候,嵌套锁就非常的有用.例如:

        static readonly object _locker = new object();
        static void Main(string[] args)
        {

            lock (_locker)
            {
                Method();
            }

        }
        static void Method()
        {
            lock (_locker)
            {
                //code
            }
        }

死锁

先看这样的代码

static readonly object locker1 = new object();
        static readonly object locker2 = new object();

        public static void MainThread()
        {
            new Thread(() =>
            {
                lock (locker1)   //获取锁locker1
                {
                    Thread.Sleep(1000);
                    lock (locker2)//尝试获取locker2
                    {
                        Console.WriteLine("locker1   locker2");
                    }
                }
            }).Start();

            lock (locker2)//获取锁loccker2
            {
                Thread.Sleep(1000);//尝试获取locker1
                lock (locker1)
                {
                    Console.WriteLine("locker2     locker1");
                }
            }
        }

分析:在这里,主线程先获取locker2的锁,然后sleep,接着尝试获取locker1的锁.副线程先获取locker1的锁,然后sleep,接着尝试获取locker2的锁.程序进入死锁状态,两个线程都在等待对方释放自己等待的锁.

CLR作为一个独立宿主环境,它不像SQL Server一样,他没有自动检测死锁机制,也不会结束一个线程来破坏死锁.死锁的线程会导致部分线程无限的等待.

小小的结一下

个人感觉那个死锁的案例,需要记住,记住代码

版权声明:本文为博主原创文章,未经博主允许不得转载。

时间: 2024-12-23 22:30:28

简单的同步的相关文章

Rsync简单的同步配置

操作系统两台:VM #Version: CentOS Linux release 7.3.1611 (Core) #Kernel: Linux 3.10.0-514.10.2.el7.x86_64 #Architecture: x86-64 #IP_Add:172.21.93.228 #IP_Add:172.21.93.229 服务端配置 Rsync版本: 安装:直接yum install rsync [[email protected] ~]# rsync --version rsync  v

简单的 同步 异步 请求

#import "ViewController.h" @interface ViewController () @property(nonatomic,strong)UITextView *textView; @property(nonatomic,copy)NSString *BASE_URL; @property(nonatomic,copy)NSString *BASE_URL1_PARAM; @property(nonatomic,strong)NSMutableData *m

超简单的同步本地git仓库到远端服务器

平台:win7:github本地目录在C:\Users\ZY\Documents\GitHub. 1.添加ssh-key到github服务器上: a).生成ssh-key:C:\Users\ZY\Documents\GitHub> ssh-keygen -t rsa -C "you-name".一路回车,就会在本地的C:\Users\ZY\下生成.ssh 文件夹.文件id_rsa.pub里面的内容就是我们需要的.好的拷贝方法是(这里拷贝不好的话会导致ssh验证失败):C:\Use

线程和同步

线程和同步 1 概述 对于所有需要等待 的操作,例 如 ,因 为文件 . 数据库或网络访 问都需要一定 的时间,此 时就可以启 动一个新线程,同时完成其他任务,即使是处理密集型的任务,线程也是有帮助的. 2 Parallel类 2.1 用Parallel.For()方法循环 Parallel.For()方法类似于C#的For循环,多次执行一个任务,它可以并行运行迭代.迭代的顺序没有定义. 1 ParallelLoopResult result = Parallel.For(0, 10, i =>

C# 多线程编程第二步——线程同步与线程安全

上一篇博客学习了如何简单的使用多线程.其实普通的多线程确实很简单,但是一个安全的高效的多线程却不那么简单.所以很多时候不正确的使用多线程反倒会影响程序的性能. 下面先看一个例子 : class Program { static int num = 1; static void Main(string[] args) { Stopwatch stopWatch = new Stopwatch(); //开始计时 stopWatch.Start(); ThreadStart threadStart

C#之任务,线程和同步

1 概述 对于所有需要等待 的操作,例 如 ,因 为文件 . 数据库或网络访 问都需要一定 的时间,此 时就可以启 动一个新线程,同时完成其他任务,即使是处理密集型的任务,线程也是有帮助的. 2 Parallel类 2.1 用Parallel.For()方法循环 Parallel.For()方法类似于C#的For循环,多次执行一个任务,它可以并行运行迭代.迭代的顺序没有定义. 1 ParallelLoopResult result = Parallel.For(0, 10, i => 2 { 3

【总结】Java线程同步机制深刻阐述

原文:http://hxraid.iteye.com/blog/667437 我们可以在计算机上运行各种计算机软件程序.每一个运行的程序可能包括多个独立运行的线程(Thread). 线程(Thread)是一份独立运行的程序,有自己专用的运行栈.线程有可能和其他线程共享一些资源,比如,内存,文件,数据库等. 当多个线程同时读写同一份共享资源的时候,可能会引起冲突.这时候,我们需要引入线程“同步”机制,即各位线程之间要有个先来后到,不能一窝蜂挤上去抢作一团. 同步这个词是从英文synchronize

同步和异步UDP使用方法

同步和异步Socket的区别是,同步Socket会阻塞当前进程,而异步Socket则不会. 首先,一个最简单的同步UDP收发程序实例.可以看到,发送调用Send()函数之后,开始调用Receive接收,这个时候程序会一直在这里等待,直到收到数据. using System; using System.Net.Sockets; using System.Net; using System.Text; public class UdpClientTest { public static void M

从一个简单的Java单例示例谈谈并发

一个简单的单例示例 单例模式可能是大家经常接触和使用的一个设计模式,你可能会这么写 public class UnsafeLazyInitiallization { private static UnsafeLazyInitiallization instance; private UnsafeLazyInitiallization() { } public static UnsafeLazyInitiallization getInstance(){ if(instance==null){ /