• 【线程呓语】与线程相关的一些概念


    上一篇文章已经介绍了,线程是对CPU的模拟和抽象,因为一台机器只有一个CPU,又要执行多个应用的代码,为了让上层应用不考虑这些细节,而使用线程这么个东西抽象一下,这样让上层应用觉得整个CPU都是它的。但CPU毕竟只有一个(或是有限的),那么就必定存在线程的切换。这也就涉及线程的状态转换:

    image

    主意这里的箭头有双向的,有单向的。

    线程在运行时可能由于需要某种资源而暂时停止运行,这称之为阻塞,当需要的资源得到满足时线程的状态会变成就绪,如果这个时候正好有CPU空闲或者正好线程调度到这个线程上,那么该线程就会马上执行。线程在运行时还有可能因为自己的时间片用完了,线程调度程序安排别的线程执行(上下文切换),而从运行状态变成就绪状态。就绪状态也可以稍后调度为运行状态。线程调度的算法有很多种,这在操作系统领域有很多文献专门来描述这个。

    死锁(deadlock)

    在写多线程相关的书籍或者文章中,恐怕死锁这个词出现的频率最高。那死锁到底是一个什么意思呢?

    为了解释这个概念,我们假设有两个线程T1和T2,有两个资源R1和R2。在某一时刻,T1拥有R1,T2拥有R2。但是这两个线程贪得无厌,T1还需要R2才能运行所以阻塞了,而T2需要R1才能运行所以也阻塞了。所以这两个线程总是不断地尝试获取永远也得不到的东西互不相让,这种状态除非有外力的介入,不然不会打破。这就是死锁。死锁的危害性非常大,有可能造成整个系统的宕机。

    我们来看下面这段程序,来演示一下如何弄出个死锁出来(该程序仅仅为了演示目的,实际中不要如此编码):

       1: using System;
       2: using System.Threading;
       3: namespace DeadLock
       4: {
       5:     class Program
       6:     {
       7:         static void Main(string[] args)
       8:         {
       9:             Test t = new Test();
      10:             Thread t1 = new Thread(t.test1);
      11:             Thread t2 = new Thread(t.test2);
      12:  
      13:             t1.Start();
      14:             t2.Start();
      15:  
      16:             Console.ReadLine();
      17:  
      18:         }
      19:     }
      20:  
      21:     public class Test
      22:     {
      23:         private object resource1 = new object();
      24:         private object resource2 = new object();
      25:  
      26:         public void test1()
      27:         {
      28:  
      29:             Console.WriteLine(string.Format("test1:Thread{0} try to get resouce1", Thread.CurrentThread.ManagedThreadId.ToString()));
      30:             lock (resource1)
      31:             {
      32:                 Console.WriteLine(string.Format("test1:Thread{0} got resouce1", Thread.CurrentThread.ManagedThreadId.ToString()));
      33:                 Thread.Sleep(500);
      34:                 Console.WriteLine(string.Format("test1:Thread{0} try to get resouce2", Thread.CurrentThread.ManagedThreadId.ToString()));
      35:                 lock (resource2)
      36:                 {
      37:                     Console.WriteLine(string.Format("test1:Thread{0} got resouce2", Thread.CurrentThread.ManagedThreadId.ToString()));
      38:                 }
      39:             }
      40:         }
      41:  
      42:         public void test2()
      43:         {
      44:             Console.WriteLine(string.Format("test2:Thread{0} try to get resouce2", Thread.CurrentThread.ManagedThreadId.ToString()));
      45:             lock (resource2)
      46:             {
      47:                 Console.WriteLine(string.Format("test2:Thread{0} got resouce2", Thread.CurrentThread.ManagedThreadId.ToString()));
      48:                 Thread.Sleep(500);
      49:                 Console.WriteLine(string.Format("test2:Thread{0} try to get resouce1", Thread.CurrentThread.ManagedThreadId.ToString()));
      50:                 lock (resource1)
      51:                 {
      52:                     Console.WriteLine(string.Format("test2:Thread{0} got resouce1", Thread.CurrentThread.ManagedThreadId.ToString()));
      53:                 }
      54:             }
      55:         }
      56:     }
      57: }

    thread1获取resource1后等待一会儿,让thread2获取到resource2,然后thread1“咬住”resource1死死不放,还去获取resource2,而thread2“咬住”resource2死死不放,去获取resource1,这样thread1和thread就像抱团一样了。

    死锁形成的必要条件:

    互斥条件:resource1和resource2只能被一个线程拥有

    占有和等待条件:已经得到resource1的thread1可以再请求resource2,thread2也可以在获取到resource2后再获取resource1

    不可抢占条件:已经分配给一个线程的资源不能强制性地被抢占(thread1已经得到了resource1,不能强制地剥夺它,除非thread1自己释放resource1,在这里就是退出lock块)

    循环等待条件:死锁发生时,系统中肯定有两个或两个以上的线程组成一条环路,该环路中的每个线程都等待着下一个线程已经占有的资源。

    与死锁相近的一个概念是活锁,或称之为饥饿

    活锁(livelock)

    说的就是某个线程或进程因为某些原因总是得不到自己需要的资源。假如现在排队买饭,饭堂规定年龄小的和年龄老的可以优先买饭,站在队伍的前面,如果总是有源源不断的人加进来,且总是 比你年龄小或年龄老,你就总是排在队伍的后头,最后饥饿而死~~

    还有一个常见的情况是,编程时为了提高效率,常常将写操作和读操作分开,比如多个线程可以同时读某一资源,但只要有一个线程写那么其他线程就不能读也不能写了,那么如果现在有很多线程在读资源A,而有一个线程来写,这样就会造成总是有线程在读,而写线程却插不上去。

    线程安全(thread safety)

    在一些框架API文档中总是会出现这么一个句子:该接口是线程安全的。那么线程安全到底是什么意思呢?

    意思就是说这个接口是否能在多线程环境下安全的调用。比如该接口可能会修改一些共享的数据,而又没有对这些共享数据加锁,那么就不能安全地在多线程环境下使用了。

  • 相关阅读:
    Xamarin.Android和UWP之MVVM的简单使用(二)
    Xamarin.Android和UWP之MVVM的简单使用(一)
    那些年用过的xUnit.net的简单用法
    Xamarin.Android之给我们的应用加点过渡效果
    Xamarin.Android之Splash的几种简单实现
    Xamarin.Android之布局文件智能提示问题
    Xamarin.Android之Spinner的简单探讨
    Xamarin.Android之封装个简单的网络请求类
    Xamarin.Android再体验之简单的登录Demo
    【分库分表】sharding-jdbc实践—分库分表入门
  • 原文地址:https://www.cnblogs.com/yuyijq/p/1938678.html
Copyright © 2020-2023  润新知