对于异步编程的使用方法的整理

2023-05-16

〇、本文是我给自己的学习做的一个总结,不保证正确性,请读到本文的朋友谨慎参考,欢迎交流,谢谢。

明确一个问题:

=======================================================================

=====对于异步编程和多线程编程而言,异步是默认的,同步是需要想办法实现的。========

=======================================================================

一、同步编程和异步编程

参考:什么是线程同步和异步-最多言

首先编程中的同步异步说法的含义与实际中的含义是不同的,比如现在有两件事,听歌和洗衣服,下面以这两件事为例说明同步和异步

1.同步编程

        同步就是指只有一条时间线,一个线程要等待上一个线程执行完之后才开始执行当前的线程。

        也就是说:听歌,洗衣,洗衣,听歌。。。。,洗衣服的时候不听歌,听歌的时候不洗衣服。

2、异步编程

        异步是指一个线程去执行,它的下一个线程不必等待它执行完就开始执行。

        也就是说,边听歌边洗衣服

二、类Task、action、function、thread  和关键字 async/await

参考文章:[.NET]Thread与Task的区别 - 大杂草 - 博客园

在做一个有关于线程的程序代码时会用到这些类与关键字,

1、Class Thread

1.1 thread究竟是同步还是异步的?

         Thread类被首先用于声明一个线程,需要明确的是用thread声明的这2个线程是异步的,这一点通过下面这个例子来证明:

namespace 线程实验
{
    class Program
    {
        public static  bool  afin = false;
        public static bool bfin = false;
        static void Main(string[] args)
        {           
            System.Diagnostics.Stopwatch st = new System.Diagnostics.Stopwatch();
            Thread a1 = new Thread(metha);
            Thread b1 = new Thread(methb);          
            st.Start();
            
            a1.Start();
            b1.Start();    
       
            while((afin==false)&&(bfin==false))         
            long time = st.ElapsedMilliseconds;
            Console.WriteLine("time is:"+time.ToString());
            Console.ReadKey();
        }
        public static void metha()
        {
            for (int i = 0; i < 10; i++)
            {
                Console.WriteLine("111");
                Console.WriteLine("22222");
                Console.WriteLine("33333333");
                Console.WriteLine("zzzzzzzzzzz");
                Thread.Sleep(1000);
            }
            afin = true;
        }
        public static void methb()
        {
            for (int i = 0; i < 10; i++)
            {
                Console.WriteLine("b");
                Thread.Sleep(1000);
            }
            bfin = true;
        }
    }
}

需要明确的是用thread声明的这2个线程是异步的,也就是说他俩各自独立、同时运行,二者互不干涉,这一点可以从下面的运行结果上看出来,即在方法metha执行的过程中methb也在执行,

 

 如2号圈中,显然在执行metha时,methb也在执行,而最后的二者时间相加也可以说明metha和methb是同时执行的,否则最后运行时间应该大于20000才对。

1.2、常见的使用thread类声明线程的方法

对于无参数方法而言:

        方法1:Thread t = new Thread(method);

        方法2:Thread t = new Thread(new  Thread(method));

对于有参数方法而言:

Thread t = new Thread(new  ParameterizedThreadStart(method));

以上语句声明了一个线程,但线程此时还只是声明完毕,还没有开始运行,运行线程的语句如下

对于无参数而言:t.Start();

对于有参数而言:t.Start(param);

现在线程开始执行了。

1.3、Class Thread的一些其他的知识点

2、Class  Action 和 Class Function

2.1、这部分的概述

        类action和类function都是委托,也就是delegate,更确切的说它们是两个用泛型标准写的两个delegate。

        之所以要将这一部分放在这里,是由于线程是通过委托来实现的,回看一下之前声明线程的语句是不是有些像声明委托的语句?

委托声明:

                public delegate void dele(string  name);

                public void    method(string name);

                dele d=method

                d("yyy");

线程声明:Thread t = new Thread(method);    

而在task类中对action和function的使用更多。

2.2、类 Action

参考资料:Action

之前说过他使用泛型实现的delegate,看一下这个图,是微软官网上截到的,都是泛型的方法实现的,声明方法是这个:

public delegate void Action<in T>(T obj);

这是一个只有一个参数的action委托。

Action<......>,是专用于无返回值的函数,与之对应的是Task<Action>。

具体的使用方法为

public delegate void Action<in T>(T obj);

这是一个只有一个参数的action委托。

                  

Action<string>   messageTarget = Console.WriteLine;

意思是声明了一个需要一个参数的委托 messageTarget,这个委托所委托的是一个Console.WriteLine()方法。

messageTarget("Hello, World!");

意思是开始运行被代理的函数

和delegate对比一下是一样的。

               public delegate void  dele(string  name);           

                dele d=Console.WriteLine

                d("yyy");

2.3、类 Function

参考资料:Func

这一部分和上面的差不多,区别在与Function是主要针对的是有返回值的函数的委托,看一下图:

 如果只有一个参数那么最后一个就是返回值,如果有两个以上参数,那么最后的一个值就是返回值。

一个无参有返的委托的声明方法:

                        public delegate TResult Func<out TResult>();

3、类 Task

 参考资料:百度安全验证

请高手们说说Task和Thread的区别_百度知道

面试必备:请问C#中Task和Thread有区别吗?如果有请简述区别_Run

task与thread的区别和使用讲解  https://www.jb51.net/article/250950.htm

3.1、Task的用途

        task是异步编程的的核心,task在线程池的基础上进行了优化,提供了更多的API,Task类专门擅长于异步操作,于是就有了一个问题,thread也是异步编程,那么thread和Task有什么区别呢?为了回答这个问题应该梳理一下Task是如何来的。        

 3.2 、Task的来历

第一步,我们首先有了thread,这个类用以创造一些线程,用以实现异步编程

第二步,在现成的基础上,人们有发明了线程池 Threadpool, Threadpool其实就是thread的集合,具有很多优势,不过在任务多的时候全局队列会存在竞争而消耗资源。

线程池比之thread有许多优点:

thread默认为前台线程,主程序必须等线程跑完才会关闭,而threadpool相反。

但线程池也有如下缺点

  • 不支持线程的取消,就像公园的摇摇车,坐上去后必须等这一圈跑完才能下来。
  • 不支持线程执行的先后次序,这个有点像车站的出租车,坐上去后司机启动车的快慢乘客是无法控制的,因此当任务多的时候全局队列会存在竞争而消耗资源。

第三步,在上述线程池的基础上发展出了Task

task简单地看就是任务,那和thread有什么区别呢?

Task的背后的实现也是使用了线程池线程,但它的性能优于ThreadPoll,因为它使用的不是线程池的全局队列,而是使用的本地队列,使线程之间的资源竞争减少。

同时Task提供了丰富的API来管理线程、控制。

但是相对前面的两种耗内存,Task依赖于CPU对于多核的CPU性能远超前两者,单核的CPU三者的性能没什么差别。

于是我们可以回答3.1提出的问题,thread和Task有什么区别呢?

  • task是根据自己需要调用线程

  • thread就是个基本单位

  • 简单地说,thread是单核多线程,task是多核多线程

  • Task是将多个操作封装成一个概念上原子操作。但这个操作由哪个Thread甚至多个Thread来处理处理你并不清楚。总之就是可以被正常完成。

  • Thread仅仅是一条线程,所有操作都是这个Thread一个人完成的。

  • Task较新,发布于.NET 4.5,能结合新的async/await代码模型写代码,它不止能创建新线程,还能使用线程池(默认)、单线程等方式编程,在UI编程领域,Task还能自动返回UI线程上下文,还提供了许多便利API以管理多个Task。

  • Task默认使用线程池,也就是后台线程:当主线程结束时,你创建所有的tasks都会结束。
  • Task.Run返回一个Task对象,可以使用它来监视其过程
  • 在Task.Run之后,我们没有调用Start,因为该方法创建的是“热”任务(hot task)
  • 可以通过task的构造函数创建“冷”任务(cold task),但开发中很少这么干
  • 通过Task的Status属性来跟踪task的执行状态。


Task属于多核开发的封装。跟单线程工作的任务有很大的区别,甚至是本质上的区别。所以可比性不大。

3.3、Task的使用方法 

        Task类的使用与委托密不可分,就像delegate委托用泛型来实现时根据被委托的函数是否有返回值可以分为Action<...>和Func<...,Tresult>两种,Task类在使用时叶根据对应的委托是否有返回值分为两种:

Task类  对应void方法Task(Action)
Task<TResult>有返回值的方法Task<TRsult>(Func<TResult>)

Task taskA = Task.Run( () => Thread.Sleep(2000));

(1)Task类

        这种一共有以下几种使用方法

using System;
using System.Threading;
using System.Threading.Tasks;

class Example
{
    static void Main()
    {
        Action<object> action = (object obj) =>
                                {
                                   Console.WriteLine("Task={0}, obj={1}, Thread={2}",
                                   Task.CurrentId, obj,
                                   Thread.CurrentThread.ManagedThreadId);
                                };

        // t1这个任务是用声明一个Task类的实例的方法instantiated。 
        Task t1 = new Task(action, "alpha");

        // t2这个任务实现用了Task.Factory.StartNew(action, "beta");这个语句。
        Task t2 = Task.Factory.StartNew(action, "beta");
        // Block the main thread to demonstrate that t2 is executing
        t2.Wait();//这里代表着要等t2结束再往下运行

        // 运行 t1 
        t1.Start();
        Console.WriteLine("t1 has been launched. (Main Thread={0})",
                          Thread.CurrentThread.ManagedThreadId);
        // Wait for the t1 task to finish.
        t1.Wait();

  // Construct a started task using Task.Run.用Task.Run这个语句构建了一个已经开始的任务t3;
        String taskData = "delta";
        Task t3 = Task.Run( () => {Console.WriteLine("Task={0}, obj={1}, Thread={2}",
                                                     Task.CurrentId, taskData,
                                                      Thread.CurrentThread.ManagedThreadId);
                                   });
        // Wait for the task t3  to finish.
        t3.Wait();

        // Construct an unstarted task
        Task t4 = new Task(action, "gamma");
        // Run it synchronously
        t4.RunSynchronously();
        // Although the task was run synchronously, it is a good practice
        // to wait for it in the event exceptions were thrown by the task.
        t4.Wait();
    }
}
// The example displays output like the following:
//       Task=1, obj=beta, Thread=3
//       t1 has been launched. (Main Thread=1)
//       Task=2, obj=alpha, Thread=4
//       Task=3, obj=delta, Thread=3
//       Task=4, obj=gamma, Thread=1

 由上面的一段程序可以知道,使用task有大概这样三种方法

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

对于异步编程的使用方法的整理 的相关文章

随机推荐