• 多线程基础知识(一)


    基础知识

    1) 一个应用程序就是一个进程,一个进程中至少有一个线程,线程可分为前台线程和后台线程。

    2) 前台线程和后台线程

    3) 一个人一边烧水一边洗衣服比“先烧水再洗衣服”效率高。同一时刻一个人只能干一件事情,其实是在“快速频繁切换”,如果处理不当可能比不用多线程效率还低。讨论多线程先只考虑“单核 cpu”。

    4) 普通的代码是从上向下执行的,但是多线程的代码可以“并行”执行,可以把“线程”理解成独立的执行单元,线程中的代码可以“并行执行”。

      线程根据情况被分配给一定的“时间片”来运行,可能一个线程还没执行完,就又要把时间片交给别的线程执行。把要在单独的线程放到一个方法中,然后创建 Thread 对象,运行它,这个 Thread中的代码就会在单独的线程中执行。

    5) 多线程的好处:

        有很大可能增加系统的运行效率;

        开发 winform 程序,避免界面卡;

        注册后向用户发送欢迎邮件,如果发送邮件很慢的话,避免注册过程很慢

     1 static void Main(string[] args)
     2         {
     3             
     4             int i = 5;
     5             //创建一个子线程
     6             Thread t1 = new Thread(() =>
     7             {
     8                 //返回到主线程中
     9                 Console.WriteLine("i="+i);
    10             });
    11             t1.Start();
    12             Console.ReadLine();
    13         }
    Thread

    参数化线程

     1 static void Main(string[] args)
     2         {
     3             
     4             int i = 5;
     5             //创建一个子线程
     6             Thread t1 = new Thread((obj) =>
     7             {//委托中添加参数
     8                 //返回到主线程中
     9                 Console.WriteLine("i=" + i);
    10                 Console.WriteLine("obj="+ obj);
    11             });
    12             //参数化
    13             t1.Start(i);
    14             Console.ReadLine();
    15         }

    创建的十个线程执行的顺序是不一定的,所以出现这样的结果

    线程睡眠

    Thread.Sleep(n)指当前代码所在的线程“睡眠 N 毫秒”

    线程退出

    前后台线程的主要区别:

        进程中只要存在没有完成的前台线程,进程就不会被销毁。

        换句话说就是,如果所有的前台线程都完成了,进程就会被销毁,即使是存在未完成任务的后台线程。

        可以通过设置Thread.IsBackground来把线程设置为前台线程或者是后台线程。

        把线程设置为“后台线程”后,所有“非后台线程”执行结束后程序就会退出,不会等“后台线程”执行结束

    线程优先级

     static void Main(string[] args)
            {
                Thread t1 = new Thread(()=> {
                    Console.WriteLine("线程优先级");
                });
                //设置线程优先级,线程默认都是Normal级
                t1.Priority = ThreadPriority.Normal;
                t1.Start();
            }
    
    
    public enum ThreadPriority
        {
            Lowest = 0,
            BelowNormal = 1,
            Normal = 2,
            AboveNormal = 3,
            Highest = 4
        }

    线程的终止

      可以调用 Thread 的 Abort 方法终止线程的执行,会在当前执行的代码上“无风起浪”的抛出 ThreadAbortException,可以 catch 一下验证一下,一般不需要程序去 catch。

    线程的同步

     1 class Program
     2     {
     3         private static int counter = 0;
     4         static void Main(string[] args)
     5         {
     6             Thread t1 = new Thread(() =>
     7             {
     8                 for (int i = 0; i < 1000; i++)
     9                 {
    10                     counter++;
    11                     Thread.Sleep(1);
    12                 }
    13             });
    14             t1.Start();
    15             Thread t2 = new Thread(() =>
    16             {
    17                 for (int i = 0; i < 1000; i++)
    18                 {
    19                     counter++;
    20                     Thread.Sleep(1);
    21                 }
    22             });
    23 
    24             t2.Start();
    25             while (t1.IsAlive) { };
    26             while (t2.IsAlive) { };
    27             Console.WriteLine(counter);
    28             Console.ReadKey();
    29         }
    30 
    31     }

    当t1线程占用counter时t2线程可能已经改变了counter的值,造成数据的不同步

      解决方案:

    ①:lock关键字:对象互斥
    ②:[MethodImpl(MethodImplOptions.Synchronized)],只能有一个线程访问
    ③:Monitor类:lock关键字最终会被编译成Monitor

     1 class Program
     2     {
     3         private static int counter = 0;
     4         private static object locker = new object();
     5         static void Main(string[] args)
     6         {
     7             Thread t1 = new Thread(() =>
     8             {
     9                 for (int i = 0; i < 1000; i++)
    10                 {
    11                     lock (locker)
    12                     {
    13                         counter++;
    14                     }
    15                     Thread.Sleep(1);
    16                 }
    17             });
    18             t1.Start();
    19             Thread t2 = new Thread(() =>
    20             {
    21                 for (int i = 0; i < 1000; i++)
    22                 {
    23                     lock (locker) { 
    24                         counter++;
    25                     }
    26                     Thread.Sleep(1);
    27                 }
    28             });
    29 
    30             t2.Start();
    31             while (t1.IsAlive) { };
    32             while (t2.IsAlive) { };
    33             Console.WriteLine(counter);
    34             Console.ReadKey();
    35         }
    36     }
    lock解决

    线程中其它的操作

    1、Interrupt 用于提前唤醒一个在 Sleep 的线程,Sleep 方法会抛出 ThreadInterruptedException 异常

     1 Thread t1 = new Thread(()=> {
     2  Console.WriteLine("t1要睡了");
     3  try
     4  {
     5  Thread.Sleep(5000);
     6  }
     7  catch(ThreadInterruptedException)
     8  {
     9  Console.WriteLine("擦,叫醒我干啥");
    10  }
    11  Console.WriteLine("t1醒了");
    12  });
    13  t1.Start();
    14  Thread.Sleep(1000);
    15  t1.Interrupt();
    View Code

    2、Sleep 是静态方法,只能是自己主动要求睡,别人不能命令他睡

    3、已经过时的方法:Suspend、Resume。不要用

    4、Abort()方法会强行终止线程,会引发线程内当前在执行的代码发出 ThreadAbortException异常

    5、t1.Join()当前线程等待 t1 线程执行结束(Join 这里翻译成“连接”:你完了我再接着)

     1 class Program
     2     {
     3         
     4         static void Main(string[] args)
     5         {
     6             Thread t1 = new Thread(() =>
     7             {
     8                 for (int i = 0; i < 100; i++)
     9                 {
    10                     Console.WriteLine("t1 " + i);
    11                 }
    12             });
    13             t1.Start();
    14             Thread t2 = new Thread(() =>
    15             {
    16                 t1.Join();//等着 t1 执行结束
    17                 for (int i = 0; i < 100; i++)
    18                 {
    19                     Console.WriteLine("t2 " + i);
    20                 }
    21             });
    22             t2.Start();
    23         }
    24     }

    单例模式与多线程

    简单实用的,可在单线程也可在多线程中使用:

    1 class God{
    2     private static God instance=new God();
    3     private God(){
    4     
    5     }
    6     public static God GetInstance(){
    7         return instance;
    8     }
    9 }

    懒汉模式:他的问题在于如果是多线程则可能有2个线程同时访问if(instance==null),则会出现问题

     1 class God{
     2     private static God instance=null;
     3     private God(){}
     4     
     5     public static God GetInstance(){
     6         if(instance==null){
     7             instance=new God();
     8         }
     9         return instance;
    10     }
    11 }

    多线程下的懒汉模式:这种情况在每次创建时都访问lock是会造成性能下降

     1 class God{
     2     private static God instance=null;
     3     private God(){}
     4     private static object locker=new object();
     5     
     6     public static God GetInstance(){
     7         lock(locker){
     8             if(instance==null){
     9                 instance=new God();
    10             }
    11             return instance;
    12         }
    13     }
    14 }

    多线程下双重锁单利:

     1 class God{
     2     private static God instance=null;
     3     private God(){}
     4     private static object locker=new object();
     5     
     6     public static God GetInstance(){
     7         if(instance==null){
     8             lock(locker){
     9                 if(instance==null){
    10                     instance=new God();
    11                 }
    12                 return instance;
    13             }
    14         }
    15     }
    16 }

    WaitHandle

      除了锁之外,.Net 中还提供了一些线程间更自由通讯的工具,他们提供了通过“信号”进行通讯的机制,通俗的比喻为“开门”、“关门”:Set()开门,Reset()关门,WaitOne()等着开门

    WaitHandle抽象类

    ManualResetEvent:手动开关

      WaitOne():开一次门

     1 ManualResetEvent mre=new ManualResetEvent(false);//默认值给的是否开门,false关门
     2 Thread t1=new Thread(()=>{
     3     cw("开始等待着开门");
     4     mre.WaitOne();//开一次门
     5     cw.("终于等到你开门");
     6 });
     7 t1.start();
     8 cw("按任意键开门");
     9 cr();
    10 mre.Set();
    11 cr();
    12 
    13 
    14 //WaitOne()还可以设置等待时间
    15 //mre.Reset()手动关门
    View Code

      WaitAll():用来等待所有信号都变为“开门状态”

      WaitAny():用来等待任意一个信号都变为“开门状态”。
    AutoResetEvent他是在开门并且一个 WaitOne 通过后自动关门,因此命名为“AutoResetEvent”(Auto 自动-Reset 关门)

     1 AutoResetEvent are = new AutoResetEvent(false);
     2 Thread t1 = new Thread(() => {
     3 while (true)
     4 {
     5 Console.WriteLine("开始等着开门");
     6 are.WaitOne();
     7 Console.WriteLine("终于等到你");
     8 }
     9 });
    10 t1.Start();
    11 Console.WriteLine("按任意键开门");
    12 Console.ReadKey();
    13 are.Set();//开门
    14 Console.WriteLine("按任意键开门");
    15 Console.ReadKey();
    16 are.Set();
    17 Console.WriteLine("按任意键开门");
    18 Console.ReadKey();
    19 are.Set();
    View Code

    ManualResetEvent 就是学校的大门,开门大家都可以进,除非主动关门;

    AutoResetEvent:就是火车地铁的闸机口,过了一个后自动关门

  • 相关阅读:
    随机验证码生成
    python之map和filter
    Json学习笔记
    动态规划求区间最值问题RMQ(Range Minimum/Maximum Query)
    积水问题
    5亿个数找中位数
    Linux下进程间通信:命名管道mkfifo
    Trie树总结
    树的公共祖先问题LCA
    类文件结构
  • 原文地址:https://www.cnblogs.com/cuijl/p/7652692.html
Copyright © 2020-2023  润新知