zoukankan      html  css  js  c++  java
  • C# 多线程编程

    01 基本概念

    多线程学习现状

    ​ 多线程,很多人工作多年,几乎没有主动用过多线程,或者用过,但是面试说不清楚,而且内心里,特别没
    底;网上资源虽多,看了觉得有道理,但是好像实践不起来

    概念

    进程:程序在服务器上运行时,占据的计算资源合集,称之为进程
    进程之间不会相互干扰-一进程间的通信比较困难(分布式)
    线程:程序执行的最小单位,响应操作的最小执行流,
    线程也包含自己的计算资源,
    线程是属于进程的,一个进程可以有多个线程
    多线程:一个进程里面,有多个线程并发执行

    C# 中的线程:

    多线程Thread:类,就是一个封装,是.NetFramework对线程对象的抽象封装
    通过Thread去完成的操作,最终是通过向操作系统请求得到的执行流
    Current Thread:当前线程--任何操作执行都是线程完成的,运行当前这句话的线程
    ManagedThreadld:是.Net平台给Thread起的名字,就是个int值,尽量不重复

    异步操作和多线程的区别

    多线程和异步操作的异同

      多线程和异步操作两者都可以达到避免调用线程阻塞的目的,从而提高软件的可响应性。甚至有些时候我们就认为多线程和异步操作是等同的概念。但是,多线程和异步操作还是有一些区别的。而这些区别造成了使用多线程和异步操作的时机的区别。

      异步操作的本质

      所有的程序最终都会由计算机硬件来执行,所以为了更好的理解异步操作的本质,我们有必要了解一下它的硬件基础。 熟悉电脑硬件的朋友肯定对DMA这个词不陌生,硬盘、光驱的技术规格中都有明确DMA的模式指标,其实网卡、声卡、显卡也是有DMA功能的。DMA就是直接内存访问的意思,也就是说,拥有DMA功能的硬件在和内存进行数据交换的时候可以不消耗CPU资源。只要CPU在发起数据传输时发送一个指令,硬件就开始自己和内存交换数据,在传输完成之后硬件会触发一个中断来通知操作完成。这些无须消耗CPU时间的I/O操作正是异步操作的硬件基础。所以即使在DOS这样的单进程(而且无线程概念)系统中也同样可以发起异步的DMA操作。

      线程的本质
      线程不是一个计算机硬件的功能,而是操作系统提供的一种逻辑功能,线程本质上是进程中一段并发运行的代码,所以线程需要操作系统投入CPU资源来运行和调度。

      异步操作的优缺点

      因为异步操作无须额外的线程负担,并且使用回调的方式进行处理,在设计良好的情况下,处理函数可以不必使用共享变量(即使无法完全不用,最起码可以减少共享变量的数量),减少了死锁的可能。当然异步操作也并非完美无暇。编写异步操作的复杂程度较高,程序主要使用回调方式进行处理,与普通人的思维方式有些初入,而且难以调试。

      多线程的优缺点
      多线程的优点很明显,线程中的处理程序依然是顺序执行,符合普通人的思维习惯,所以编程简单。但是多线程的缺点也同样明显,线程的使用(滥用)会给系统带来上下文切换的额外负担。并且线程间的共享变量可能造成死锁的出现。

      适用范围

      在了解了线程与异步操作各自的优缺点之后,我们可以来探讨一下线程和异步的合理用途。我认为:当需要执行I/O操作时,使用异步操作比使用线程+同步I/O操作更合适。I/O操作不仅包括了直接的文件、网络的读写,还包括数据库操作、Web Service、HttpRequest以及.Net Remoting等跨进程的调用。
      而线程的适用范围则是那种需要长时间CPU运算的场合,例如耗时较长的图形处理和算法执行。但是往往由于使用线程编程的简单和符合习惯,所以很多朋友往往会使用线程来执行耗时较长的I/O操作。这样在只有少数几个并发操作的时候还无伤大雅,如果需要处理大量的并发操作时就不合适了。
      实例研究
      说了那么理论上的东西,可能有些兄弟早就不耐烦了,现在我们来研究几个实际的异步操作例子吧。
      实例1:由delegate产生的异步方法到底是怎么回事?

      大家可能都知道,使用delegate可以"自动"使一个方法可以进行异步的调用。从直觉上来说,我觉得是由编译器或者CLR使用了另外的线程来执行目标方法。到底是不是这样呢?让我们来用一段代码证明一下吧。

    using System;
    using System.Threading;
    
    namespace AsyncDelegateDemo
    {
      delegate void AsyncFoo(int i);
      class Program
      {
        ///<summary>
        /// 输出当前线程的信息
        ///</summary>
       ///<param name="name">方法名称</param>
    
        static void PrintCurrThreadInfo(string name)
        {
          Console.WriteLine("Thread Id of " + name+ " is: " + Thread.CurrentThread.ManagedThreadId+ ", current thread is "
          + (Thread.CurrentThread.IsThreadPoolThread ? "" : "not ")
          + "thread pool thread.");
        }
    
        ///<summary>
        /// 测试方法,Sleep一定时间
        ///</summary>
        ///<param name="i">Sleep的时间</param>
        static void Foo(int i)
        {
           PrintCurrThreadInfo("Foo()");
           Thread.Sleep(i);
        }
    
        ///<summary>
        /// 投递一个异步调用
        ///</summary>
        static void PostAsync()
        {
          AsyncFoo caller = new AsyncFoo(Foo);
          caller.BeginInvoke(1000, new AsyncCallback(FooCallBack), caller);
        }
    
        static void Main(string[] args)
        {
          PrintCurrThreadInfo("Main()");
          for(int i = 0; i < 10 ; i++)
          {
             PostAsync();
          }
          Console.ReadLine();
        }
    
        static void FooCallBack(IAsyncResult ar)
        {
          PrintCurrThreadInfo("FooCallBack()");
          AsyncFoo caller = (AsyncFoo) ar.AsyncState;
          caller.EndInvoke(ar);
        }
      }
    }  
    

    这段代码代码的输出如下:

    Thread Id of Main() is: 1, current thread is not thread pool thread.
    Thread Id of Foo() is: 3, current thread is thread pool thread.
    Thread Id of FooCallBack() is: 3, current thread is thread pool thread.
    Thread Id of Foo() is: 3, current thread is thread pool thread.
    Thread Id of Foo() is: 4, current thread is thread pool thread.
    Thread Id of Foo() is: 5, current thread is thread pool thread.
    Thread Id of FooCallBack() is: 3, current thread is thread pool thread.
    Thread Id of Foo() is: 3, current thread is thread pool thread.
    Thread Id of FooCallBack() is: 4, current thread is thread pool thread.
    Thread Id of Foo() is: 4, current thread is thread pool thread.
    Thread Id of Foo() is: 6, current thread is thread pool thread.
    Thread Id of FooCallBack() is: 5, current thread is thread pool thread.
    Thread Id of Foo() is: 5, current thread is thread pool thread.
    Thread Id of Foo() is: 7, current thread is thread pool thread.
    Thread Id of FooCallBack() is: 3, current thread is thread pool thread.
    Thread Id of Foo() is: 3, current thread is thread pool thread.
    Thread Id of FooCallBack() is: 4, current thread is thread pool thread.
    Thread Id of FooCallBack() is: 6, current thread is thread pool thread.
    Thread Id of FooCallBack() is: 5, current thread is thread pool thread.
    Thread Id of FooCallBack() is: 7, current thread is thread pool thread.
    Thread Id of FooCallBack() is: 3, current thread is thread pool thread.  
    

      从输出可以看出,.net使用delegate来"自动"生成的异步调用是使用了另外的线程(而且是线程池线程)。

    02 简单的例子

    异步多线程

    //异步多线程多会使用委托
    private void btnAsync_Click(object sender, EventArgs e)
    {
    	Console. WriteLine ();
    	Console. WriteLine ("******************btnAsync_Click 异步方法 start {0}********************",Thread. CurrentThread. ManagedThreadld);
    	Action<string> action = this.DoSomethingLong;   //Action是C# 的一个内置的委托类型,表示返回值是void,参数是泛型T的委托;
    	action.Invoke ("btnAsync_Click_l");  //委托执行,同步
    	action("btnAsync_Click_2");          //委托执行,同步
    	action.Beginlnvoke ("btnAsync_Click_3", null, null) ;  //委托执行,异步
    	Console.writeLine ("******************btnAsync_Click 异步方法 end {0}********************",Thread. CurrentThread. ManagedThreadld);
    	Console.WriteLine ();
    )
        
    private void DoSomethingLong(String a){
        Console.WriteLine ("******************方法所在线程 {0}********************",Thread. CurrentThread. ManagedThreadld);
    }
    

    有个疑问,如果beginlnvoke上万次,理论上开启了多少个线程?

    beginlnvoke本质是调用了ThreadPool里的线程,ThreadPool中线程是有上限的

    假如想没上限的使用,可以用Thread,但电脑会死机

    03 异步多线程特点

    1.同步单线程方法卡界面------ 主(UI)线程忙于计算,所以不能响应
    异步多线程方法不卡界面一一计算任务交给子线程,主(UI)线程已经闲置,可以响应别的操作
    cs:按钮后能不卡死一上传文件不卡死
    bs:用户注册发邮件/发短信/写日志

    2.同步单线程方法慢―-因为只有一个线程计算
    异步多线程方法快--因为多个线程并发计算
    多线程就是用资源换性能,但并不是线性增长
    1个线程13000毫秒 5个线程4269毫秒 性能只有3倍提升
    a多线程的协调管理额外成本一--项目经理
    b资源也有上限的一-5辆车只有3条道
    线程并不是越多越好,

    3.无序性一不可预测性
    启动无序:几乎同一时间像操作系统请求线程,也是个需要CPU处理的请求
    因为线程是操作系统资源,CLR只能去申请,具体是什么顺序,无法掌控
    执行时间不确定:同一个线程同一个任务耗时也可能不同
    其实跟操作系统的调度策略有关,
    CPU分片(计算能力太强,Is分拆1000份儿,宏观上就变成了并发的)
    那任务执行过程就得看运气了一--线程的优先级可以影响操作系统的调度
    结束无序:以上加起来

    04 简单控制线程顺序

    1. 异步回调

    委托异步调用后执行回调函数,回调函数是在子线程内执行的

    AsyncCallback callback = ar =>
    {
          Console.WriteLine(ar.AsyncState);
          Console.WriteLine("btnAsyncAdvanced_Click操作已经完成了。。。${Thread. CurrentThread. ManagedThreadld}");
    };
    //第二个参数是个委托,定义回调函数,第三个参数是各object是传递给回调的ar.AsyncState
    action.Beginlnvoke("btnAsyncAdvanced_Click", callback, "sunday");
    

    2. IsCompleted属性

    希望一方面文件上传,完成后才预览;另一方面,还希望有个进度提示一只有主线程才能操作界面;

    有等待,但会有时间上误差

    
    IAsyncResult asyncResult = action.Beginlnvoke("文件上传",null, null);
    
    int i = 0;
    while(!asyncResult.IsCompleted) //该属性用来描述异步动作是否完成,其实一开始该属性就为false,等异步动作完成会会修改该属性为true
    {
          if(i < 9)
    	  {
                this.ShowConsoleAndView($"当前文件上传进度为{++i * 10}% ...");
          }
          else
          {
                this.ShowConsoleAndView($"当前文件上传进度为99. 999%...");
    	  }
          Thread.Sleep(200);
    }
    Console.WriteLine("完成文件上传,执行预览,绑定到界面");
    
    
    private void ShowConsoleAndView(string text)
    {
         Console. WriteLine(text) ;
         this.IblProcessing.Text = text; //实际过程中,只有上面的Console在实时更新,下面这句需要等到UI线程闲下来才能更新
    }
    

    3. 信号量

    可以做超时,等待

    
    var asyncResult = action.Beginlnvoke("调用接口",null, null);
    Console. WriteLine ("Do Something Else.......");         // 发起异步调用后,再用信号量阻塞当前线程和同步的区别就在这里,可以在中间做其他操作
    Console. WriteLine ("Do Something Else........");
    Console. WriteLine ("Do Something Else........");
    Console. WriteLine ("Do Something Else.......");
    Console. WriteLine ("Do Something Else........");
    asyncResult.AsyncWaitHandle.WaitOne0 ;"阻塞当前线程,直到收到信号量,从asyncResult发出, 无延迟
    //asyncResult. AsyncWaitHandle. WaitOne(-1) ;//一直等待
    //asyncResult. AsyncWaitHandle. WaitOne (1500);"阻塞当前线程,等待且最多只等待1000ms,超过就过时了 这个就是用来做超时控制,微服务架构,一个操作需要调用5个接口,如果臬个接口很慢,会影响整个流程,可以做超时控制,超时就换接口或者放弃或者给个结果
    Console. WriteLine ("接口调用成功,必须是真实的成功。。");
    
    

    4. EndInvoke 异步调用获取返回值

    调用远程接口获取返回值

    本质也是信号量

    public void main(){
        Func<int> func = this.RemoteService; //Func也是各内置委托类
        IAsyncResult asyncResult = func.Beginlnvoke(ar => 
        {
    		int iResult4 = func.EndInvoke(ar);
    	}, null);//异步调用结果,描述异步操作的
        //int iResult3 = func.Endlnvoke(asyncResult);   //EndInvoke可以放在回调里,也可以放在主线程中,但只允许放在一个地方,不能两个同时存在
        //如果想获取异步调用真实返回值,只能EndInvoke
        
        /*{    //其他的Func委托类使用
        	Func<string> func2 = () => Dat eTime. Now. ToStringO ;
        	string sResult = func2. Endlnvoke(func2. Beginlnvoke(null, null)); 
        }
        {
        	Func<string, string> func2 = s => $〃l+{s}〃;
        	string sResult = func2. Endlnvoke (func2. Beginlnvoke(z,Jasonz,, null, null));
        )*/
    }
    
    
    /// <summary>
    ///模拟的远程接口
    /// </summary>
    /// <returns></returns>
    private int RemoteService()
    {
    	long IResult = 0;
    	for (int i = 0; i < 1000000000; i++)
    		IResult += i;
        
    	return DareTime.Now.Day;
    }
    

    05 多线程发展历史

    //NetFramework 1.0 1.1
    ThreadStart threadStart = ()=> 
    {
    	Console. WriteLine ($"This is Thread Start{Thread. CurrentThread. ManagedThreadld}");
    	Thread.Sleep(2000);
    	Console. WriteLine ($"This is Thread End {Thread. CurrentThread. ManagedThreadld}");
    };
    Thread thread = new Thread(threadStart);
    thread.Start ();
    
    //thread. Suspend 0; 挂起线程
    //thread. Resume (); 恢复线程
    //thread. JoinO ;    线程等待
    //thread. IsBackground  是否是后台线程
    //thread. Abort ();     线程销毁
    //Thread. ResetAbort();  线程销毁恢复
    //“Thread的API特别丰富,可以玩的很花哨,但是其实玩不好--因为线程资源是操作系统管理的, 
    //响应并不灵敏,所以没那么好控制
    //“Thread启动线程是没有控制的,可能导致死机
    //“Thread就很像给一个四岁的小孩一把热武器,威力很大,但是可能造成更大的破坏
    
    //------------------------------------------------------------------------------------------------------
    
    //. NetFramework 2.0(新的CLR) ThreadPool:池化资源管理设计思想,线程是一种资源,之前每 次要用线程,就去申请一个线程,使用完之后,释放掉;池化就是做一个容器,容器提前申请5 个线程,程序需要使用线程,直接找容器获取,用完后再放回容器(控制状态),避免频繁的申 请和销毁;容器自己还会根据限制的数量去申请和释放;
    //I线程复用2可以限制最大线程数量
    WaitCallback callback = o => 
    {
    	Console.WriteLine($"This is ThreadPool Start{Thread. CurrentThread. ManagedThreadld}");
        Thread.Sleep (2000);
    	Console.WriteLine ($"This is ThreadPool End{Thread. CurrentThread. ManagedThreadld}");
    }
    ThreadPool.QueueUserWorkltem(callback);
    //ThreadPool. set
    //API又太少了,线程等待顺序控制特别弱,MRE,影响了实战
    
    //------------------------------------------------------------------------------------------------------
    
    //. NetFramework 3.0 Task被称之为多线程的最佳实践!,同时后续版本一直在升级Task 1 Task线程全部是线程池线程 2 提供了丰富的API,非常适合开发实践
    Action action = 0 =>
    {
    	Console.WriteLine ($"This is Task Start{Thread. CurrentThread. ManagedThreadld}");
        Thread.Sleep(2000);
    	Console.WriteLine ($"This is Task End {Thread.CurrentThread.ManagedThreadld}");
    }
    Task task = new Task(action);
    task.Start();
    
    
    //------------------------------------------------------------------------------------------------------
    
    //并行编程,Parallel可以启动多线程,主线程也参与计算,节约一个线程;
    //可以通过Paralleloptions轻松控制最大并发数量,应用场景工作流转出时的处理
    
    Parallel.Invoke(0 =>
    {
    	Console.WriteLine ($"This is Task Start{Thread. CurrentThread. ManagedThreadld}");
        Thread.Sleep(2000);
    	Console.WriteLine ($"This is Task End {Thread.CurrentThread.ManagedThreadld}");
    },0 =>
    {
    	Console.WriteLine ($"This is Task Start{Thread. CurrentThread. ManagedThreadld}");
        Thread.Sleep(2000);
    	Console.WriteLine ($"This is Task End {Thread.CurrentThread.ManagedThreadld}");
    })  
    
    //------------------------------------------------------------------------------------------------------
    
    //async await 本质是个语法糖 
    

    PS:委托的beginInvoke在.net core 下不支持

    06 Task专题

    简单体验

    //除了明白场景,还要知道线程问题,
    //下面用多线程是为了提升效率,有严格时间限制的,先后顺序的,只能单线程,因为这些任务是可以独立并发执行的 
    //一个数据库查询 10 条数据需要 10s ,能不能多线程优化? 不能,任务不能分割
    //一个操作要查询数据库,要调用接口,要读硬盘文件?可以,任务之间相互独立 
    
    Console.WriteLine("大家开始干活了");
    
    List<Task> tasks = new List<Task>();
    
    tasks.add(Task.Run(() => this.Coding(‘小明’,"开发Portal")));
    tasks.add(Task.Run(() => this.Coding(‘小红’,"开发WebApi")));
    tasks.add(Task.Run(() => this.Coding(‘小海’,"开发前台")));
    
    //阻塞当前线程,直到任一任务完成
    Task.WaitAny(tasks.toArray());
    
    Console.WriteLine("活干好一部分了");
    
    //阻塞当前线程,直到所有任务完成
    Task.WaitAll(tasks.toArray()); //等待线程执行完
    
    Console.WriteLine("活干好了");
    

    PS:尽量不要线程套线程

    上面的Task.WaitAny和Task.WaitAll都会阻塞线程,使用如下方式就可以解决

    TaskFactory tf = new TaskFactory();
    //等待任一任务完成后,启动一个新的task来完成后续动作
    tf.ContinueWhenAny(tasks.toArray(),t=>{
        Console.WriteLine("活干好一部分了");
    })
    //等待所有任务完成后,启动一个新的task来完成后续动作    
    tf.ContinueWhenAll(tasks.toArray(),t=>{
        Console.WriteLine("活干好了");
    })
        
    //continue的后续线程,可能是新线程,也可能是刚完成任务的线程,还可能是同一个线程,但不可能是主线程
    

    task执行完后回调

    Task task = Task.Run(()=>{
      Console.WriteLine("线程干活");
    })
    //执行回调
    task.ContinueWith(t=>{
    	Console.WriteLine("线程干完活了");
    })
    

    07 线程安全

    定义:一段代码,单线程执行和多线程执行结果不同,就是线程安全问题

    一个简单的线程安全演示

    Console.WriteLine($"这是主线程启动了,主线程id:{Thread.CurrentThread.ManagedThreadId}");
    
    for(int i = 0; i < 5; i++)
    {
        Task.Run(()=> { 
            Console.WriteLine($"这是{i}启动了,当前线程id:{Thread.CurrentThread.ManagedThreadId}");
            Thread.Sleep(2000);
            Console.WriteLine($"这是{i}结束了,当前线程id:{Thread.CurrentThread.ManagedThreadId}");
        });
    }
    
    Thread.Sleep(3000);
    Console.WriteLine($"这是主线程结束了,主线程id:{Thread.CurrentThread.ManagedThreadId}");
    

    输出结果:

    这是主线程启动了,主线程id:1
    这是4启动了,当前线程id:5
    这是4启动了,当前线程id:6
    这是5启动了,当前线程id:4
    这是5启动了,当前线程id:9
    这是5启动了,当前线程id:10
    这是5结束了,当前线程id:5
    这是5结束了,当前线程id:6
    这是5结束了,当前线程id:9
    这是5结束了,当前线程id:4
    这是5结束了,当前线程id:10
    这是主线程结束了,主线程id:1
    

    上面有个bug:为什么打印出来的结果显示的是1个4和4个5,而不是1、2、3、4、5?

    因为Task.run只是创建线程,并让线程处于就绪状态,至于什么时候执行线程,是CPU调度的问题,线程执行的时候i的值是并不是我们想象中的1、2、3、4、5,而且所有的线程都共享同一个i,

    上面的程序小改下:

    Console.WriteLine($"这是主线程启动了,主线程id:{Thread.CurrentThread.ManagedThreadId}");
    
    for(int i = 0; i < 5; i++)
    {
        int k = i;
        Task.Run(()=> { 
            Console.WriteLine($"这是{i}启动了,当前线程id:{Thread.CurrentThread.ManagedThreadId}");
            Thread.Sleep(2000);
            Console.WriteLine($"这是{i}结束了,当前线程id:{Thread.CurrentThread.ManagedThreadId}");
        });
    }
    
    Thread.Sleep(3000);
    Console.WriteLine($"这是主线程结束了,主线程id:{Thread.CurrentThread.ManagedThreadId}");
    

    这时候输出结果为:

    这是主线程启动了,主线程id:1
    这是5,0启动了,当前线程id:6
    这是5,3启动了,当前线程id:8
    这是5,1启动了,当前线程id:5
    这是5,4启动了,当前线程id:7
    这是5,2启动了,当前线程id:4
    这是5,0结束了,当前线程id:6
    这是5,1结束了,当前线程id:5
    这是5,4结束了,当前线程id:7
    这是5,2结束了,当前线程id:4
    这是5,3结束了,当前线程id:8
    这是主线程结束了,主线程id:1
    

    我们发现i还是像上面说的一样,但是k却按照1、2、3、4、5的顺序来了,这又是为什么呢?

    因为所有的线程都共享一个i,但所有的线程并不共享k,假如把int k的声明放到for循环外面则是共享同一个k了,作用域的问题

    另一个简单的线程安全演示

    List<int> list = new List<int>();
    for (int i = 0; i < 10000; i++) {
        Task.Run(() => {
            list.Add(i);
            });
        }
    Thread.Sleep(5000);
    Console.WriteLine($"list集合长度{list.Count}");
    

    这时候输出结果为:

    list集合长度9939
    

    这就有问题了,为什么不是10000?

    因为list本身是个数组,在内存上是连续摆放的,假如同一时刻,去增加一个数据,都是操作同一个内存未知,2个cpu同时发了命令,内存先执行一个再执行一个,就出现覆盖

    Lock

    解决上面问题,有个简单粗暴的方法,使用lock

    		static void Main(string[] args)
            {
                List<int> list = new List<int>();
                for (int i = 0; i < 10000; i++)
                {
                    Task.Run(() => {
                        lock(LOCK)
                        { 
                            list.Add(i);
                        }
                    });
                }
                Thread.Sleep(5000);
                Console.WriteLine($"list集合长度{list.Count}");
            }
    
            private static readonly object LOCK = new object();
    

    加lock就能解决线程安全问题--就是单线程化--Lock就是保证方法块儿任意时刻只有一个线程能进去,其他线程就排队一一单线程化

    lock关键字,本质是个语法糖,

    lock(LOCK){
    
    }
    
    //等价于
    Monitor.Enter(LOCK);
    {
    
    }
    Monitor.Exit(LOCK);
    

    意思是占据一个引用,在内存中,引用类型是存放在堆里的,线程资源栈里存放指向该堆地址的指针,lock就是占据该引用,因此lock不能用数值类型,也不能用null

    假如lock的是字符串,字符串值一样,那么线程间也是相互阻塞的,因为字符串在C#中是享元的,在堆里是同一个,即使变量不一样,但他们指向的地址都是同一个,lock锁的是引用

    08 await/async

    初识

    /// <summary>
    /// await/async:是个新语法,出现C#5.0 .NetFramework在4.5及以上(CLR4.0)
    /// 是一个语法糖,不是一个全新的异步多线程使用方式,
    /// (语法糖:就是编译器提供的新功能)
    /// 本身并不会产生新的线程,但是依托于Task而存在,所以程序执行时也是有多线程的
    /// </summary>
    
    public class AwaitAsyncClassNew
    {
        public async Task DoSomething()
        {
        	await Task.Run(()=>
        	Console.WiiteLine("*************");
        })
    }
    

    初识2

    用了async/await 之后,原本没有返回值的,可以返回Task,原本有返回值T的,可以返回Task

    await关键字后面的代码,相当于是await task的回调

    namespace async和await演示1
    {
        public class AsyncAndAwaitDemo
        {
            public void Show()
            {
                Console.WriteLine($"Show 方法Start,所属线程:{Thread.CurrentThread.ManagedThreadId}");
    
                NoReturn2();
    
                Console.WriteLine($"Show 方法End,所属线程:{Thread.CurrentThread.ManagedThreadId}");
            }
    
            #region 没有返回值的
    
            /// <summary>
            /// 一个普通的多线程
            /// </summary>
            public void NoReturn()
            {
                Console.WriteLine($"NoReturn 方法Start,所属线程:{Thread.CurrentThread.ManagedThreadId}");
    
                Task.Run(() =>
                {
                    Console.WriteLine($"NoReturn 方法 Task Start,所属线程:{Thread.CurrentThread.ManagedThreadId}");
    
                    Thread.Sleep(1000);
    
                    Console.WriteLine($"NoReturn 方法 Task End,所属线程:{Thread.CurrentThread.ManagedThreadId}");
                });
    
                Console.WriteLine($"NoReturn 方法End,所属线程:{Thread.CurrentThread.ManagedThreadId}");
            }
    
            /// <summary>
            /// 原本有返回值T的,可以返回Task<T>
            /// </summary>
            /// <returns></returns>
            public async Task NoReturn2()
            {
                Console.WriteLine($"NoReturn 方法Start,所属线程:{Thread.CurrentThread.ManagedThreadId}");
    
                //调用线程创建新线程执行内部操作
                Task task = Task.Run(() =>
                {
                    Console.WriteLine($"NoReturn 方法 Task Start,所属线程:{Thread.CurrentThread.ManagedThreadId}");
    
                    Thread.Sleep(1000);
    
                    Console.WriteLine($"NoReturn 方法 Task End,所属线程:{Thread.CurrentThread.ManagedThreadId}");
                });
                //让调用线程回去忙自己的事情,用了await后,相当于将await后面的代码包装成一个回调,可以用同步编码的形式来写异步
                await task;
                Console.WriteLine($"NoReturn 方法End,所属线程:{Thread.CurrentThread.ManagedThreadId}");
            }
    
            #endregion
    
            #region 带返回值的
    
            public long ReturnLong()
            {
                Console.WriteLine($"ReturnLong 方法Start,所属线程:{Thread.CurrentThread.ManagedThreadId}");
    
                long result = 0;
    
                Task.Run(() =>
                {
                    Console.WriteLine($"ReturnLong 方法 Task Start,所属线程:{Thread.CurrentThread.ManagedThreadId}");
                    for (int i = 0; i < 100000000; i++)
                    {
                        result++;
                    }
    
                    Console.WriteLine($"ReturnLong 方法 Task End,所属线程:{Thread.CurrentThread.ManagedThreadId}");
                });
    
                Console.WriteLine($"ReturnLong 方法End,所属线程:{Thread.CurrentThread.ManagedThreadId}");
    
                return result;
            }
    
            /// <summary>
            /// 
            /// </summary>
            /// <returns></returns>
            public async Task<long> ReturnLong2()
            {
                Console.WriteLine($"ReturnLong2 方法Start,所属线程:{Thread.CurrentThread.ManagedThreadId}");
    
                long result = 0;
    
                await Task.Run(() =>
                {
                    Console.WriteLine($"ReturnLong2 方法 Task Start,所属线程:{Thread.CurrentThread.ManagedThreadId}");
                    
                    //for (int i = 0; i < 100000000; i++)
                    //{
                    //    result++;
                    //}
    
                    Console.WriteLine($"ReturnLong2 方法 Task End,所属线程:{Thread.CurrentThread.ManagedThreadId}");
                    //return result;
                });
    
                Console.WriteLine($"ReturnLong2 方法End,所属线程:{Thread.CurrentThread.ManagedThreadId}");
    
                return result;
            }
    
            #endregion
        }
    }
    

    09 其他

    死锁、STAThread等

  • 相关阅读:
    Pie(二分)
    Prime Ring Problem + nyoj 素数环 + Oil Deposits + Red and Black
    关于各种排列(dfs)
    精讲N皇后问题
    (转)女生应该找一个玩ACM的男生
    Radar Installation(贪心,可以转化为今年暑假不ac类型)
    vector之妙用系列
    寻找最大数(贪心)
    Linux使用tcpdump命令抓包并使用wireshark分析
    使用MSF发现主机和端口扫描
  • 原文地址:https://www.cnblogs.com/zhenhunfan2/p/14447496.html
Copyright © 2011-2022 走看看