1.2线程基础(二)

来源:互联网 发布:hive sql 语法 编辑:程序博客网 时间:2024/06/03 17:22

1.2.1 lock关键字

使用lock关键字来确保当一个线程使用某些资源时,同时其他线程无法使用该资源.

class Counter{    private readonly object _syncRoot = new object();    public int Count { get; private set; }    public void Add()    {        lock (_syncRoot)        {            Count++;        }    }    public void Sub()    {        lock (_syncRoot)        {            Count--;        }    }}var c = new Counter();//创建多个线程来访问cThread t1 = new Thread(()=> TestCounter(c));Thread t2 = new Thread(() => TestCounter(c));Thread t3 = new Thread(() => TestCounter(c));Thread t4 = new Thread(() => TestCounter(c));Thread t5 = new Thread(() => TestCounter(c));t1.Start();t2.Start();t3.Start();t4.Start();t5.Start();t1.Join();t2.Join();t3.Join();t4.Join();t5.Join();Console.WriteLine("count:"+c.Count);

最后输出count:0,有人可能会说这不是很正常吗,那请自己动手把Counter类中lock部分都移除再试一遍,最后输出就会不一样了!~
变成这样:

public void Add(){    Count++;}public void Sub(){    Count--;}

这是因为如果去掉lock部分,Counter类就不是线程安全的.
当多个线程同时访问counter对象时,

第一个线程得到的counter值是10并增加为11,
第二个线程得到的值是11并增加为12,
第一个线程得到counter值12,但递减操作发生前,
第二个线程得到counter值也是12,
第一个线程将12递减为11并存回counter中,
同时第二个线程进行了同样的操作.

结果我们进行了两次两次递增操作但只有第一递减操作,
这显然是不对的.
这种情形被称为竞争条件(race condition),竞争条件是多线程环境中导致错误的常见原因.

为了确保不会发生以上情形,必须保证当有线程操作counter对象时,所有其他线程必须等待直到当前线程完成操作.我们可以使用lock关键字来实现这种行为.如果锁定了一个对象,需要访问该对象的所有其他线程则会处于阻塞状态,并等待知道该对象解除锁定,但这可能会导致严重的性能问题.

1.2.2 使用Monitor类锁定资源

死锁(deadlock)也是多线程编程中常见的错误.由于死锁将导致程序停止工作,可以使用Monitor类来避免死锁.

class Program{    static void Main(string[] args)    {        object l1 = new object();        object l2 = new object();        new Thread(()=> LockObject(l1,l2)).Start();        lock (l2)        {            Console.WriteLine("Monitor.TryEnter 可以避免死锁,在超时后返回false");            Thread.Sleep(1000);            if (Monitor.TryEnter(l1,TimeSpan.FromSeconds(5)))            {                Console.WriteLine("成功获取到一个受保护的资源!");            }            else            {                Console.WriteLine("获取资源超时!");            }        }        Console.WriteLine("------------------------------------");        new Thread(() => LockObject(l1, l2)).Start();        lock (l2)        {            Console.WriteLine("这将是一个死锁!");            Thread.Sleep(1000);            lock (l1)            {                Console.WriteLine("成功获取到一个受保护的资源!");            }        }        Console.WriteLine("End.");        Console.ReadKey();    }    static void LockObject(object l1,object l2)    {        lock (l1)        {            Thread.Sleep(1000);            lock (l2);        }    }}

输出(最后因为进入死锁,所以End.未打印)

1.2.3 处理异常

在线程中始终使用try/catch代码块是非常重要的,因为不可能在线程代码之外来捕获异常.

class Program{    static void Main(string[] args)    {        var t = new Thread(ThreadOne);        t.Start();        t.Join();        try        {            t = new Thread(ThreadTwo);            t.Start();        }        catch (Exception ex)        {            Console.WriteLine($"Main:{ex.Message}");        }    }    static void ThreadOne()    {        try        {            Console.WriteLine("Starting a faulty thread...");            Thread.Sleep(1000);            throw new Exception("Boom!!!");        }        catch (Exception ex)        {            Console.WriteLine($"ThreadOne:{ex.Message}");        }    }    static void ThreadTwo()    {        Console.WriteLine("Starting a faulty thread...");        Thread.Sleep(1000);        throw new Exception("Boom!!!");    }}

输出

由上图可见ThreadOne的异常被捕获,但是ThreadTwo的异常未被捕获

原创粉丝点击