zoukankan      html  css  js  c++  java
  • C#基础系列——多线程 信号量 异步 编程 Task Thread async和await

     多线程:

    ThreadStart 是一个委托函数

            static void Main(string[] args)
            {
                Thread oGetArgThread = new Thread(new ThreadStart(() =>
                {
    
                    for (var i = 0; i < 1000000; i++)
                    {
                        Console.WriteLine("后台线程计数" + i);
                        Thread.Sleep(100);
                    }
                }));
                oGetArgThread.IsBackground = true;
                oGetArgThread.Start();
                Console.ReadKey();
            }

    Task:

    1、Task对象的前世今生:Task对象是.Net Framework 4.0之后出现的异步编程的一个重要对象。在一定程度上来说,Task对象可以理解Thread对象的一个升级产品。既然是升级产品,那它肯定有他的优势,比如我们上面Thread对象不能解决的问题:对于有返回值类型的委托。Task对象就能简单的解决。

    复制代码
         static void Main(string[] args)
            {
                Console.WriteLine("执行GetReturnResult方法前的时间:" + DateTime.Now.ToString("yyyy-MM-dd hh:MM:ss"));
                var strRes = Task.Run<string>(() => { return GetReturnResult(); });//启动Task执行方法
                Console.WriteLine("执行GetReturnResult方法后的时间:" + DateTime.Now.ToString("yyyy-MM-dd hh:MM:ss"));
                Console.WriteLine(strRes.Result);//得到方法的返回值
                Console.WriteLine("得到结果后的时间:" + DateTime.Now.ToString("yyyy-MM-dd hh:MM:ss"));
    
                Console.ReadLine();
            }
    
            static string GetReturnResult()
            {
                Thread.Sleep(2000);
                return "我是返回值";
            }
    复制代码

    先来看结果:

    从结果分析可知在执行var strRes = Task.Run<string>(() => { return GetReturnResult(); })这一句后,主线程并没有阻塞去执行GetReturnResult()方法,而是开启了另一个线程去执行GetReturnResult()方法。直到执行strRes.Result这一句的时候主线程才会等待GetReturnResult()方法执行完毕。为什么说是开启了另一个线程,我们通过线程ID可以看得更清楚:

    复制代码
         static void Main(string[] args)
            {
                Console.WriteLine("执行GetReturnResult方法前的时间:" + DateTime.Now.ToString("yyyy-MM-dd hh:MM:ss"));
                var strRes = Task.Run<string>(() => { return GetReturnResult(); });
                Console.WriteLine("执行GetReturnResult方法后的时间:" + DateTime.Now.ToString("yyyy-MM-dd hh:MM:ss"));
                Console.WriteLine("我是主线程,线程ID:" + Thread.CurrentThread.ManagedThreadId);
                Console.WriteLine(strRes.Result);
                Console.WriteLine("得到结果后的时间:" + DateTime.Now.ToString("yyyy-MM-dd hh:MM:ss"));
    
                Console.ReadLine();
            }
    
            static string GetReturnResult()
            {
                Console.WriteLine("我是GetReturnResult里面的线程,线程ID:" + Thread.CurrentThread.ManagedThreadId);
                Thread.Sleep(2000);
                return "我是返回值";
            }
    复制代码

    结果:

    由此可以得知,Task.Run<string>(()=>{}).Reslut是阻塞主线程的,因为主线程要得到返回值,必须要等方法执行完成。

    Task对象的用法如下:

    复制代码
                //用法一
                Task task1 = new Task(new Action(MyAction));
              //用法二
                Task task2 = new Task(delegate
                {
                    MyAction();
                });
            //用法三
                Task task3 = new Task(() => MyAction());
                Task task4 = new Task(() =>
                {
                    MyAction();
                });
    
                task1.Start();
                task2.Start();
                task3.Start();
                task4.Start();
    复制代码

    由上可知,Task对象的构造函数传入的是一个委托,既然能传入Action类型的委托,可想而知Action的16中类型的参数又可以派上用场了。于是乎Task对象参数的传递就不用多说了吧。详见 C#基础系列——委托和设计模式(一)里面Action委托的用法。

    初识 async & await。

    复制代码
            static void Main(string[] args)
            {
                Console.WriteLine("我是主线程,线程ID:{0}", Thread.CurrentThread.ManagedThreadId);
                TestAsync();
                Console.ReadLine();
            }
    
            static async Task TestAsync()
            {
                Console.WriteLine("调用GetReturnResult()之前,线程ID:{0}。当前时间:{1}", Thread.CurrentThread.ManagedThreadId, DateTime.Now.ToString("yyyy-MM-dd hh:MM:ss"));
                var name = GetReturnResult();
                Console.WriteLine("调用GetReturnResult()之后,线程ID:{0}。当前时间:{1}", Thread.CurrentThread.ManagedThreadId, DateTime.Now.ToString("yyyy-MM-dd hh:MM:ss"));
                Console.WriteLine("得到GetReturnResult()方法的结果:{0}。当前时间:{1}", await name, DateTime.Now.ToString("yyyy-MM-dd hh:MM:ss"));
            }
    
            static async Task<string> GetReturnResult()
            {
                Console.WriteLine("执行Task.Run之前, 线程ID:{0}", Thread.CurrentThread.ManagedThreadId);
                return await Task.Run(() =>
                {
                    Thread.Sleep(3000);
                    Console.WriteLine("GetReturnResult()方法里面线程ID: {0}", Thread.CurrentThread.ManagedThreadId);
                    return "我是返回值";
                });
            }
    复制代码

    结果:

    我们来看看程序的执行过程:

    由上面的结果可以得到如下结论:

    (1)在async标识的方法体里面,如果没有await关键字的出现,那么这种方法和调用普通的方法没什么区别。

    (2)在async标识的方法体里面,在await关键字出现之前,还是主线程顺序调用的,直到await关键字的出现才会出现线程阻塞。

    (3)await关键字可以理解为等待方法执行完毕,除了可以标记有async关键字的方法外,还能标记Task对象,表示等待该线程执行完毕。所以await关键字并不是针对于async的方法,而是针对async方法所返回给我们的Task。

    (4)是否async关键字只能标识返回Task对象的方法呢。我们来试试:

    异步方法的返回类型必须为void、Task或者Task<T>类型。也就是说async要么是void,要么和Task关联。

    3、除了await关键字,Task对象还有另外一种方式等待执行结果。  

    复制代码
         static async Task TestAsync()
            {
                Console.WriteLine("调用GetReturnResult()之前,线程ID:{0}。当前时间:{1}", Thread.CurrentThread.ManagedThreadId, DateTime.Now.ToString("yyyy-MM-dd hh:MM:ss"));
                var name = GetReturnResult();
                Console.WriteLine("调用GetReturnResult()之后,线程ID:{0}。当前时间:{1}", Thread.CurrentThread.ManagedThreadId, DateTime.Now.ToString("yyyy-MM-dd hh:MM:ss"));
                Console.WriteLine("得到GetReturnResult()方法的结果:{0}。当前时间:{1}", name.GetAwaiter().GetResult(), DateTime.Now.ToString("yyyy-MM-dd hh:MM:ss"));
            }
    复制代码

    这样可以得到相同的结果。

    name.GetAwaiter()这个方法得到的是一个TaskAwaiter对象,这个对象表示等待完成的异步任务的对象,并提供结果的参数。所以除了能完成await关键字的等待之外,它还能做一些其他的操作。我们将TaskAwaiter转到定义

    复制代码
    public struct TaskAwaiter<TResult> : ICriticalNotifyCompletion, INotifyCompletion
        {
            public bool IsCompleted { get; }
            public TResult GetResult();
            public void OnCompleted(Action continuation);
            public void UnsafeOnCompleted(Action continuation);
       }    
    复制代码

    IsCompleted:获取一个值,该值指示异步任务是否已完成。

    GetResult():得到执行的结果。这个方法和await关键字效果相同。

    OnCompleted():传入一个委托,在任务执行完成之后执行。

    UnsafeOnCompleted():计划与此 awaiter 相关异步任务的延续操作。

    由此可以看出,await关键字实际上就是调用了TaskAwaiter对象的GetResult()方法。

     信号量:

    同时只允许一定数量的线程执行,这个数量称为信号量

    using System;
    using System.Threading;
    using System.Threading.Tasks;
    
    namespace MyTTCon
    {
        class mysemaphore
        {
            public static void Main()
            {
                for (int i = 0; i < 20; i++)
                {
                    new mythread().run(i.ToString());
                }
                Console.ReadKey();
            }
        }
        class mythread
        {
            //public Thread thrd;
            //创建一个可授权2个许可证的信号量,且初始值为2
           static Semaphore sem = new Semaphore(2, 2);
           public  void run(string Name)
            {
                
                
                Task.Run(() =>
                {
                    //申请一个许可证
                    Console.WriteLine(Thread.CurrentThread.ManagedThreadId + ":正在等待一个许可证……");
                    sem.WaitOne();
                    Console.WriteLine(Thread.CurrentThread.ManagedThreadId + ":申请到许可证……");
                    Thread.Sleep(8000);
                    Console.WriteLine(Thread.CurrentThread.ManagedThreadId + ":释放许可证……");
                    sem.Release();
                });
            }
        }
    
    }

     
    http://www.cnblogs.com/landeanfen/p/4704139.html

  • 相关阅读:
    用户登录
    在ASP.NET里实现计算器代码的封装
    计算器的封装
    典型用户和场景-老陈、小石头
    葫芦娃团队
    20155235 王玥 《基于Arm实验箱的接口测试和应用》 课程设计报告
    实验补交专用链接随笔
    20155235 《网络攻防》 实验九 Web安全基础
    20155235 《网络攻防》 实验七 网络欺诈防范
    20155235 《网络攻防》 实验八 Web基础
  • 原文地址:https://www.cnblogs.com/xiaoshi657/p/9091370.html
Copyright © 2011-2022 走看看