c# Task异步使用

这篇具有很好参考价值的文章主要介绍了c# Task异步使用。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

描述
Task出现之前,微软的多线程处理方式有:Thread→ThreadPool→委托的异步调用,虽然可以满足基本业务场景,但它们在多个线程的等待处理方面、资源占用方面、延续和阻塞方面都显得比较笨拙,在面对复杂的业务场景下,显得有点捉襟见肘
Task是微软在.Net 4.0时代推出来的,也是微软极力推荐的一种多线程的处理方式,Task看起来像一个Thread,实际上,它是在ThreadPool的基础上进行的封装
Task的控制和扩展性很强,在线程的延续、阻塞、取消、超时等方面远胜于Thread和ThreadPool
Task可以简单看作相当于Thead+TheadPool,其性能比直接使用Thread要更好,在工作中更多的是使用Task来处理多线程任务
任务Task和线程Thread的区别
Task是建立在Thread之上的,最终其实还是由Thread去执行,它们都是在 System.Threading 命名空间下的
Task跟Thread并不是一对一的关系。比如说开启10个任务并不一定会开启10个线程,因为使用Task开启新任务时,是从线程池中调用线程,这点与ThreadPool.QueueUserWorkItem类似

Task的使用


创建Task的三种方式

方式一:通过创建Task对象后调用其 Start()函数
方式二:调用Task的静态方法Run()
方式三:通过Task工厂,新建一个线程
// 方式一,通过Start
Task t1 = new Task(() => { Console.WriteLine("我是Task方式一"); });
t1.Start();
 
// 方式二,通过Run
Task t2= Task.Run(()=>{Console.WriteLine("我是Task方式二"); });
 
// 方式三,通过工厂
Task t3= Task.Factory.StartNew(()=>{Console.WriteLine("我是Task方式三"); });


带返回值与不带返回值的Task
Task无返回值: 接收的是Action委托类型
Task<TResult>有返回值:接收的是Func<TResult>委托类型

static void Main()
{
    // 没有返回参数
    Task t1 = new Task(() => { Console.WriteLine("我是Task没有返回参数"); });
    t1.Start();
 
    // 有返回参数
    Task<int> t2 = new Task<int>(() => { return 1+1; });
    t2.Start();
    int result = t2.Result;
    Console.WriteLine(result);
}


输出结果

我是Task没有返回参数
2
一次性建立多个任务场景

static void test1()
{
    Task[] taskArray = new Task[10];
    for (int i = 0; i < 10; i++)
    {
        int bb = i;
        Task t = Task.Run(() => { Console.WriteLine("任务ID:{0}, 结果:{1}",Thread.CurrentThread.ManagedThreadId, bb); });
        taskArray[i] = t;
    }
    // 等待所有任务完成
    Task.WaitAll(taskArray);
}


输出结果

任务ID:4, 结果:0
任务ID:10, 结果:4
任务ID:7, 结果:1
任务ID:8, 结果:2
任务ID:10, 结果:7
任务ID:11, 结果:5
任务ID:9, 结果:3
任务ID:12, 结果:6
任务ID:7, 结果:8
任务ID:8, 结果:9
Task阻塞的三种方式
Wait(): 等待单个线程任务完成  
WaitAll():来指定等待的一个或多个线程结束
WaitAny():来指定等待任意一个线程任务结束

static void test3()
{
    // 方式一: wait方法
    Task t = Task.Run(() => { Console.WriteLine("方式1:任务1......"); }) ;
    // 等待 上述任务完成
    t.Wait();
    Console.WriteLine("方式一结束..........");
 
    // 方式二: waitAll 方法
    Task tt = Task.Run(() => { Console.WriteLine("方式2:任务1......"); });
    Task tt2 = Task.Run(() => { Console.WriteLine("方式2:任务2......"); });
    Task.WaitAll(tt,tt2);
    Console.WriteLine("方式二结束..........");
 
    // 方式三:waitAny 方法
    Task ttt = Task.Run(() => { Console.WriteLine("方式3:任务1......"); });
    Task ttt2 = Task.Run(() => { Console.WriteLine("方式3:任务2......"); });
    Task.WaitAny(ttt, ttt2);
    Console.WriteLine("方式三结束..........");
 
}

输出结果

方式1:任务1......
方式一结束..........
方式2:任务1......
方式2:任务2......
方式二结束..........
方式3:任务2......
方式3:任务1......
方式三结束..........
Task任务的延续
WhenAll().ContinueWith() :作用是当WhenAll()中指定的线程任务完成后再执行ContinueWith()中的任务,也就是线程任务的延续。而由于这个等待是异步的,因此不会给主线程造成阻塞
WhenAll(task1,task2,...):Task的静态方法,作用是异步等待指定任务完成后,返回结果。当线程任务有返回值时,返回Task<TResult[]>对象,否则返回Task对象。
WhenAny():用法与WhenAll()是一样的,不同的是只要指定的任意一个线程任务完成则立即返回结果。
ContinueWith():Task类的实例方法,异步创建当另一任务完成时可以执行的延续任务。也就是当调用对象的线程任务完成后,执行ContinueWith()中的任务

static void test4()
{
    Task t = Task.Run(() => { Thread.Sleep(1000); Console.WriteLine("我是线程任务....."); });
    // 异步创建延续任务
    Task.WhenAll(t).ContinueWith((data) => { Console.WriteLine("我是延续任务...."); });
 
    Console.WriteLine("这是主线程........");
    Console.ReadKey();
 
}


输出结果

这是主线程........
我是线程任务.....
我是延续任务....
注:Task任务的延续 与 上面阻塞相比,主要的好处就是 延续是异步的不会阻塞主线程

Task的父子任务
TaskCreationOptions.AttachedToParent:用于将子任务依附到父任务中

static void test5()
{
    // 建立一个父任务
    Task parentTask = new Task(() => {
        // 创建两个子任务,依附在父任务上
        Task.Factory.StartNew(() => { Console.WriteLine("子task1任务。。。。。。"); }, TaskCreationOptions.AttachedToParent);
        Task.Factory.StartNew(() => { Console.WriteLine("子task2任务。。。。。。"); }, TaskCreationOptions.AttachedToParent);
        Thread.Sleep(1000);
        Console.WriteLine("我是父任务........");
    });
    parentTask.Start();
    parentTask.Wait();
    Console.WriteLine("这里是主线程.......");
    Console.ReadKey();
}

输出结果

子task2任务。。。。。。
子task1任务。。。。。。
我是父任务........
这里是主线程.......
Task中的任务取消
Task中的取消功能使用的是CanclelationTokenSource,即取消令牌源对象,可用于解决多线程任务中协作取消和超时取消
CancellationToken Token:CanclelationTokenSource类的属性成员,返回CancellationToken对象,可以在开启或创建线程时作为参数传入。
IsCancellationRequested:表示当前任务是否已经请求取消。Token类中也有此属性成员,两者互相关联。
Cancel():CanclelationTokenSource类的实例方法,取消线程任务,同时将自身以及关联的Token对象中的IsCancellationRequested属性置为true。
CancelAfter(int millisecondsDelay):CanclelationTokenSource类的实例方法,用于延迟取消线程任务。
取消任务的两种情况
情况一:通过Cancel()方法
情况二:通过CancelAfter(milliseconds) 方法

static void test6()
{
 
    // 情况一: 直接取消
    // 创建取消令牌源对象
    CancellationTokenSource cst = new CancellationTokenSource();
    //第二个参数传入取消令牌
    Task t = Task.Run(() => {
        while (!cst.IsCancellationRequested)
        {
            Thread.Sleep(500);
            Console.WriteLine("情况一,没有接收到取消信号......");
        }
    }, cst.Token);
 
    Thread.Sleep(1000);
    //1秒后结束
    cst.Cancel(); 
    Console.ReadKey();
 
 
    // 情况二: 延迟取消
    CancellationTokenSource cst2 = new CancellationTokenSource();
    Task t2 = Task.Run(() => {
        while (!cst2.IsCancellationRequested)
        {
            Console.WriteLine("情况二,没有接收到取消信号......");
        }
    }, cst2.Token);
    //1秒后结束
    cst2.CancelAfter(1000);
    Console.ReadKey();
 
}


Task跨线程访问界面控件
通过 TaskScheduler.FromCurrentSynchronizationContext()   获取TaskScheduler,并将其放入Task的start() 方法中 或 放入延续方法中即可
放入start() 方法中 
放入 ContinueWith() 延续方法中
// 通过start方法

private void button1_Click(object sender, EventArgs e)
{
    Task t = new Task(() =>
    {
        // 为界面控件赋值
        this.textBox1.Text = "线程内赋值";
    });
   
    task.Start(TaskScheduler.FromCurrentSynchronizationContext());
}
 
 
// 通过延续方法
private void button1_Click(object sender, EventArgs e)
{
        Task.Run(() =>
        {
            Thread.Sleep(1000);
        }).ContinueWith(t => {
            this.textBox1.Text = "线程内赋值";
        }, TaskScheduler.FromCurrentSynchronizationContext());
}


Task的异常处理
异常捕获
Task线程的异常处理不能直接将线程对象相关代码try-catch来捕获,需要通过调用线程对象的wait()函数来进行线程的异常捕获
线程的异常会聚合到AggregateException异常对象中(AggregateException是专门用来收集线程异常的异常类),多个异常 需要通过遍历该异常对象来获取异常信息
如果捕获到线程异常之后,还想继续往上抛出,就需要调用AggregateException对象的Handle函数,并返回false。(Handle函数遍历了一下AggregateException对象中的异常)
 

static void test7()
{
    Task t = Task.Run(() =>
    {
        throw new Exception("异常抛出.....");
    });
 
    try
    {
        t.Wait();
    }
    catch (AggregateException ex)
    {
        Console.Error.WriteLine(ex.Message);
 
        foreach (var item in ex.InnerExceptions)
        {
            Console.WriteLine("内异常:"+item.Message);
        }
        //将异常往外抛出
        // ex.Handle(p => false);
    }
    Console.ReadKey();
}


输出结果

One or more errors occurred. (异常抛出.....)
内异常:异常抛出.....文章来源地址https://www.toymoban.com/news/detail-682120.html

到了这里,关于c# Task异步使用的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 在C#中使用SQLite数据库

    轻量级桌面程序数据库不太适合用SQLServer、MySQL之类的重量级数据库,嵌入式数据库更好。在对比Access、SQLite、Firebird数据库后发现SQLite较另外两个有较多优点。 环境:.NET Framework 3.5、windows11 64位、Visual Studio 2010.  C#使用SQLite需要从SQLite官网下载DLL组件。 我是windows11,64位的

    2023年04月23日
    浏览(34)
  • 献给转java的c#和java程序员的数据库orm框架

    一个好的程序员不应被语言所束缚,正如我现在开源java的orm框架一样,如果您是一位转java的c#程序员,那么这个框架可以带给你起码没有那么差的业务编写和强类型体验。如果您是一位java程序员,那么该框架可以提供比 Mybatis-Plus 功能更加丰富、性能更高,更加轻量和完全免费的体

    2024年02月05日
    浏览(39)
  • C#学习系列之登录界面的简单数据库使用

    最近在练习界面的处理,在编写某登录界面的过程中采用到数据库的使用,简单的用户名与密码登录,在自己安装、创建数据库的表后,采用C#调用数据库,却一直会出现异常。因为在使用过程中采用了is_validation=1,反倒限制了try-catch的异常具体报错。通过此篇来记录一下数

    2024年02月04日
    浏览(41)
  • C# 使用EntityFramework CodeFirst 创建PostgreSQL数据库

    1.先创建一个ASP.Net Web应用程序,选择Web API  2、创建EntityLib、EF、AppService三个类库。EntityLib用于存放数据库表所对应的实体,AppService用于编写用户对实体的一些操作方法,如增删改查等操作。  创建好所有类库之后,需要添加引用库EntityFramework6.Npgsql,右击项目中的引用——

    2024年02月16日
    浏览(44)
  • 【C# .NET 】使用 Entity Framework Core 操作sqlite数据库

    添加包 EF Core design package   NuGet Gallery | Home 使用用于 EF Core 迁移和现有数据库中的反向工程(基架)的工具需要安装相应的工具包: 可在 Visual Studio 包管理器控制台中使用的 PowerShell 工具的 Microsoft.EntityFrameworkCore.Tools 跨平台命令行工具的 dotnet-ef 和 Microsoft.EntityFramewor

    2024年02月14日
    浏览(53)
  • C#中使用LINQtoSQL管理SQL数据库之添加、修改和删除

    目录 一、添加数据 二、修改数据 三、删除数据  四、添加、修改和删除的源码 五、生成效果 1.VS和SSMS原始记录 2.删除ID=2和5的记录 3.添加记录ID=2、5和8  4.修改ID=3和ID=4的记录          用LINQtoSQL管理SQL Server数据库时,主要有添加、修改和删除3种操作。         项目

    2024年02月05日
    浏览(78)
  • Python异步编程之web框架 异步vs同步 数据库IO任务并发支持对比

    主题: 比较异步框架和同步框架在数据库IO操作的性能差异 python版本 :python 3.8 数据库 :mysql 8.0.27 (docker部署) 压测工具 :locust web框架 :同步:flask 异步:starlette 请求并发量 : 模拟10个用户 服务器配置 : Intel(R) i7-12700F 客户端配置 :Intel(R) i7-8700 3.20GHz python中操作数据库通常

    2024年02月08日
    浏览(58)
  • 【fly-iot飞凡物联】(18):配置Emqx的webhook,编写http接口,完成设备状态的更新。显示在线/离线状态,异步插入数据库,使用supervisor启动

    fly-iot飞凡物联专栏: https://blog.csdn.net/freewebsys/category_12219758.html https://www.bilibili.com/video/BV19a4y127Gt/ 【fly-iot】(7):配置Emqx的webhook,编写http接口,完成设备状态的更新。显示在线/离线状态,异步插入数据库,使用supervisor启动 然后就可以在工具中进行测试了: 直接设置接口

    2024年01月15日
    浏览(57)
  • Python异步编程之web框架 异步vs同步 数据库IO任务压测对比

    主题: 比较异步框架和同步框架在数据库IO操作的性能差异 python版本 :python 3.8 数据库 :mysql 8.0.27 (docker部署) 压测工具 :locust web框架 :同步:flask 异步:starlette 请求并发量 : 模拟10个用户 服务器配置 : Intel(R) i7-12700F 客户端配置 :Intel(R) i7-8700 3.20GHz python中操作数据库通常

    2024年02月08日
    浏览(92)
  • 使用Java连接SQLserver数据库

    使用win10、SQLServer2012、eclipse2020-6、Java1.8.0_311    下载sqljdbc_6.0.8112.200_chs.tar.gz,再将其解压sqljdbc_6.0.8112.200_chs.tar  这里x64是在适于64位,x86是适于32位,选择合适自己电脑的,这里我的是64位的计算机,选择x64文件中的文件,下如图。 将此文件sqljdbc_auth.dll复制到D:Javajdk1.8.

    2024年02月08日
    浏览(40)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包