博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
多线程基础知识(一)
阅读量:4581 次
发布时间:2019-06-09

本文共 7600 字,大约阅读时间需要 25 分钟。

基础知识

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:就是火车地铁的闸机口,过了一个后自动关门

 

转载于:https://www.cnblogs.com/cuijl/p/7652692.html

你可能感兴趣的文章
压缩文件批量处理(附Python脚本代码)
查看>>
栅格数据的批量镶嵌(附Python脚本)
查看>>
关于guns
查看>>
20190919-01克隆虚拟机 000 007
查看>>
20190916-01linux文件与目录结构 000 001
查看>>
20190917-02Linux网络配置 000 003
查看>>
20190918-03关机重启命令及修改root密码 000 006
查看>>
20190917-01VI/VIM编辑器 000 002
查看>>
20190918-01配置主机名 000 004
查看>>
20190919-02安装Xshell和CRT远程工具 000 008
查看>>
20190918-02关闭防火墙服务 000 005
查看>>
20190923-01Linux帮助命令 000 009
查看>>
20190923-02Linux文件目录类 000 010
查看>>
20190923-03Linux时间日期类 000 011
查看>>
20190923-04Linux用户管理命令 000 012
查看>>
20190923-05Linux用户组管理命令 000 013
查看>>
20190923-06Linux文件权限类 000 014
查看>>
20190923-07Linux搜索查找类 000 015
查看>>
20190923-08Linux压缩和解压类 000 016
查看>>
20190925-01安装redis 000 022
查看>>