C# Task中的Func, Action, Async与Await的使用

在说Asnc和Await之前,先说明一下Func和Action委托, Task任务的基础的用法

1. Func

Func是一种委托,这是在3.5里面新增的,2.0里面我们使用委托是用Delegate,Func位于System.Core命名空间下,使用委托可以提升效率,例如在反射中使用就可以弥补反射所损失的性能。

Action<T>和Func<T,TResult>的功能是一样的,只是Action<T>没有返类型,

Func<T,T,Result>:有参数,有返回类型
Action,则既没有返回也没有参数,

Func<T,TResult>
的表现形式分为以下几种:

1。Func<T,TResult>
2。Func<T,T1,TResult>
3。Func<T,T1,T2,TResult>
4。Func<T,T1,T2,T3,TResult>
5。Func<T,T1,T2,T3,T4,TResult>

分别说一下各个参数的意义,TResult表示
委托所返回值 所代表的类型, T,T1,T2,T3,T4表示委托所调用的方法的参数类型,

以下是使用示例:

复制代码
 1 Func<int, bool> myFunc = null;//全部变量
 2 
 3 myFunc = x => CheckIsInt32(x); 
 4 //给委托封装方法的地方 使用了Lambda表达式
 5 
 6 private bool CheckIsInt32(int pars)//被封装的方法
 7 {
 8   return pars == 5;
 9 }
10 
11 bool ok = myFunc(5);//调用委托
复制代码


MSDN:http://msdn.microsoft.com/zh-cn/library/bb534303(VS.95).aspx

2. Action

但是如果我们需要所封装的方法不返回值,增么办呢?就使用Action!

可以使用
Action<T1, T2, T3, T4>委托以参数形式传递方法,而不用显式声明自定义的委托。封装的方法必须与此委托定义的方法签名相对应。也就是说,封装的方法必须具有四个均通过值传递给它的参数,并且不能返回值。(在 C# 中,该方法必须返回 void。在 Visual Basic 中,必须通过 Sub…End Sub 结构来定义它。)通常,这种方法用于执行某个操作。

使用方法和Func类似!

Action:既没有返回,也没有参数,使用方式如下:

1 Action action = null;//定义action
2 
3 action =  CheckIsVoid;//封装方法,只需要方法的名字
4 
5 action();//调用

 

MSDN:http://msdn.microsoft.com/zh-cn/library/bb548654(VS.95).aspx

总结:

使用Func<T,TResult>和Action<T>,Action而不使用Delegate其实都是为了简化代码,使用更少的代码达到相同的效果,不需要我们显示的声明一个委托。

Func<T,TResult>的最后一个参数始终是返回类型,而Action<T>是没有返回类型的,而Action是没有返回类型和参数输入的.

 

3. Task

使用 Async 和 Await 的异步编程(msdn的好文章)https://msdn.microsoft.com/zh-cn/library/hh191443(v=vs.120)

Task基本使用: C# 线程知识--使用Task执行异步操作

Task和await结合返回值见: .NET(C#):await返回Task的async方法

Task深入了解见: C# Task的使用

 

总结: Task提供一种新的类似多线程的多任务的用法, 达到使用线程的效果,但具体怎么分配线程由.net底层控制,这样性能好,效率也高,我们只需要专注业务逻辑, 同时具备以前线程不具备的返回值的功能(可以通过回调事件来处理),而在Task中只是简单的使用await来使用, await的task相当于同步的调用task, 同时会有返回值,只是在书写方面需要注意async, task,func, action等等关键字的使用,新手可能很困惑, 下面的例子就是一个简单的task使用例子,新手注意注释!!!

复制代码
 1 static void Main(string[] args)
 2 {
 3     //异步方法, 当中使用await可以执行task异步任务,否则当作同步执行
 4     test();
 5     //其次输出,因为异步进入task任务了,就同步执行当前线程下面的代码
 6     log("Main:调用test后");
 7     Thread.Sleep(Timeout.Infinite); 
 8 }
 9 
10 //Main方法不允许加async,所以我们用这个方法使用await
11 static async void test()
12 {
13     //最先输出,还没有进入task
14     log("test: await之前");
15     // await后的内容会被加在目标doo的Task的后面,然后test马上返回,而doo则是进入了另一个任务执行了
16     log("doo的Task的结果: " + await doo());
17     // 会等到上面await执行的任务完成后才会执行当前代码
18     log("test: await之后");
19 }
20 //返回Task的async方法, 一个标准的带返回值的异步task任务方法
21 static async Task<int> doo()
22 {
23     // 简单的说, async中使用await就是异步中以同步方式执行Task任务的方法,task任务一个接一个执行.
24     var res1 = await Task.Run(() => { Thread.Sleep(1000); log("Awaited Task1 执行"); return 1; });
25     var res2 = await Task.Run(() => { Thread.Sleep(1000); log("Awaited Task2 执行"); return 2; });
26     var res3 = await Task.Run(() => { Thread.Sleep(1000); log("Awaited Task3 执行"); return 3; });
27 
28     //不使用await:线程池多线程, 当前task不会等这个执行完,因为不是await,只是又开启了一个线程
29     ThreadPool.QueueUserWorkItem(_ =>
30     {
31         Thread.Sleep(1000);
32         log("ThreadPool.QueueUserWorkItem: 线程池多线程执行");
33     });
34 
35     //不使用await:Task多线程, 当前task不会等这个执行完,因为不是await,只是又开启了一个任务
36     Task.Run(() =>
37     {
38         Thread.Sleep(1000);
39         log("Task.Run: Task多线程执行");
40 
41     });
42    
43     return res1 + res2 + res3;
44 }
45 //输出方法:显示当前线程号和输出信息
46 static void log(string msg)
47 {
48     Console.WriteLine("线程{0}: {1}", Thread.CurrentThread.ManagedThreadId, msg);
49 }
复制代码

 

执行结果如下:

 

4. Async、Await

这个是.NET 4.5的特性,所以要求最低.NET版本为4.5。

看很多朋友还是使用的Thread来使用异步多线程操作,基本上看不见有使用Async、Await进行异步编程的。各有所爱吧,其实都可以。只要正确使用就行,不过还是写了这篇文章推荐大家使用Async、Await。 原因就是:可以跟写同步方法一样去异步编程。代码则就非常的清晰,就跟写普通的代码一样,不用关系如何去异步编程,也让很多初级程序员也能够异步编程了。下面是一个使用Thread 多线程实现的异步例子,以及一个使用Async与Await的异步例子,接下来我们再简单理解下Async与Await的相关技术说明。

Thread多线程异步编程例子

复制代码
class Program
{
    static void Main(string[] args)
    {
        Console.WriteLine("主线程测试开始..");
        Thread th = new Thread(ThMethod);
        th.Start();
        Thread.Sleep(1000);
        Console.WriteLine("主线程测试结束..");
        Console.ReadLine();
    }
 
 
    static void ThMethod()
    {
        Console.WriteLine("异步执行开始");
        for (int i = 0; i < 5; i++)
        {
            Console.WriteLine("异步执行" + i.ToString() + "..");
            Thread.Sleep(1000);
        }
        Console.WriteLine("异步执行完成");
    }
}
复制代码

 

  

以上代码运行效果如下图

 

 

使用Async与Await进行异步编程

 

复制代码
 1 static void Main(string[] args)
 2 {
 3     Console.WriteLine("主线程测试开始..");
 4     AsyncMethod();
 5     Thread.Sleep(1000);
 6     Console.WriteLine("主线程测试结束..");
 7     Console.ReadLine();
 8 }
 9 
10 static async void AsyncMethod()
11 {
12     Console.WriteLine("开始异步代码");
13     var result = await MyMethod();
14     Console.WriteLine("异步代码执行完毕" + result.ToString());
15 }
16 
17 static async Task<int> MyMethod()
18 {
19     for (int i = 0; i < 5; i++)
20     {
21         Console.WriteLine("异步执行" + i.ToString() + "..");
22         await Task.Delay(1000); //模拟耗时操作
23     }
24     return 100;
25 }
复制代码

 

运行效果:

 

显而易见我们就跟写同步方法一样,完成了异步方法的编写,代码更清晰了。

只有拥有async才能在其内部使用await关键字。异步方法可以具有Task、Task<>或void的返回类型;

await关键字则是用于返回值是“可等待”类型(awaitable)的方法

 

posted on 2017-09-23 13:36  v.e.n.u.s 阅读( ...) 评论( ...) 编辑 收藏

转载于:https://www.cnblogs.com/jx270/p/7580948.html

<think>好的,用户想了解如何在C#使用Taskasync/await来替代传统的多线程方法。首先,我需要回顾一下用户之前的问题和我的回答。之前用户询问的是在WinForm中多线程调用有返回值的函数,我介绍了使用异步委托和Thread类的方法。现在用户想转向更现代的异步编程模型,也就是Taskasync/await。 根据用户提供的引用内容,特别是引用[2]和[4],他们提到了使用async/await来编写异步方法,并且可以返回值,比如返回Task<int>。用户可能已经了解了一些基础,现在需要更深入的示例和对比,以展示如何用这些新方法替代旧的多线程方式。 首先,我需要解释Taskasync/await的基本概念,以及它们相比传统多线程的优势,比如更简洁的代码、更好的可维护性和避免回调地狱。然后,通过具体的示例来演示如何改造之前的多线程代码为异步模式。 用户可能想知道如何处理带有返回值的异步方法,以及如何在UI线程中安全地更新控件。根据之前的回答,使用Control.Invoke或BeginInvoke是必要的,但在async/await中,由于会自动回到原始上下文(比如UI线程),所以可能不需要显式调用Invoke,但需要确认这一点是否正确。 接下来,我需要对比传统Thread和异步Task的差异,可能包括资源管理、异常处理、取消支持等方面。例如,Task提供了更丰富的API,如Task.Run、ContinueWith,以及取消令牌等,这些都是传统线程所缺乏的。 还要注意用户提到的引用[3],其中提到了无返回值的异步方法,返回Task类型,而引用[4]则强调async方法的返回值必须是void、TaskTask<T>。因此,在示例中需要涵盖这两种情况,即有无返回值的方法。 在示例部分,我应该提供两个案例:一个是替换Thread的示例,另一个是替换异步委托的示例。每个案例都先展示传统方法,再用async/await改写,并说明改进点和优势。例如,使用async/await后,代码结构更清晰,避免了嵌套的回调,异常处理更直接等。 另外,需要提到异常处理的方式,在异步方法中使用try-catch,这传统多线程中的处理方式不同,因为异步方法可以通过await自动传播异常,而不需要在线程中手动捕获再传递。 最后,总结关键优势,如简化代码、自动线程上下文切换、集成取消功能等,并确保用户理解在UI应用程序中使用async/await时,如何自动回到UI线程,避免跨线程访问控件的问题。 可能还需要考虑用户在实际开发中可能遇到的问题,比如长时间运行的任务是否适合用Task.Run,或者如何组合多个异步任务。不过根据当前问题,可能先集中在基本示例和对比上,确保用户能够理解核心概念。</think>在C#中,使用`Task`和`async/await`可以更高效、简洁地替代传统多线程编程。以下是具体实现方法及对比示例: --- ### 一、`Task``async/await`核心优势 1. **简化代码结构**:避免回调嵌套,代码逻辑更清晰 2. **自动线程管理**:由线程池自动分配资源,减少手动创建线程的开销 3. **无缝上下文切换**:自动返回原始线程上下文(如UI线程)[^4] 4. **异常处理集中化**:通过`try-catch`直接捕获异步操作中的异常 --- ### 二、替代传统多线程的典型场景 #### 场景1:替换`Thread`类实现异步操作 **传统多线程方式**: ```csharp private void StartThread_Click(object sender, EventArgs e) { new Thread(() => { int result = LongRunningMethod(); this.Invoke(new Action(() => lblResult.Text = $"结果:{result}")); }).Start(); } private int LongRunningMethod() { Thread.Sleep(2000); // 阻塞主线程 return DateTime.Now.Second; } ``` **异步编程改进版**: ```csharp private async void StartTask_Click(object sender, EventArgs e) { int result = await Task.Run(() => LongRunningAsyncMethod()); lblResult.Text = $"结果:{result}"; // 自动返回UI线程上下文[^4] } private int LongRunningAsyncMethod() { Task.Delay(2000).Wait(); // 非阻塞等待 return DateTime.Now.Second; } ``` **对比改进**: - 使用`Task.Run`代替显式线程创建 - 通过`await`自动处理线程上下文切换 - UI更新无需手动调用`Invoke` --- #### 场景2:替换异步委托(BeginInvoke)实现带返回值操作 **传统异步委托方式**: ```csharp Func<int> func = () => { /*...*/ }; func.BeginInvoke(asyncResult => { int result = func.EndInvoke(asyncResult); this.BeginInvoke(() => lblResult.Text = $"结果:{result}"); }, null); ``` **异步编程改进版**: ```csharp private async void StartAsyncOperation_Click(object sender, EventArgs e) { int result = await CalculateResultAsync(); lblResult.Text = $"结果:{result}"; } private async Task<int> CalculateResultAsync() { await Task.Delay(1000); // 模拟异步操作[^2] return DateTime.Now.Second; } ``` **对比改进**: - 消除回调嵌套,代码可读性提升 - 使用`Task<T>`直接封装返回值 - 避免资源泄漏风险(自动管理Task生命周期) --- ### 三、关键语法特性详解 #### 1. 方法定义规则 - 异步方法必须用`async`修饰 - 返回值只能是`void`/`Task`/`Task<T>`[^4] ```csharp // 无返回值 async Task DoSomethingAsync() { /*...*/ } // 有返回值 async Task<int> GetValueAsync() { /*...*/ } ``` #### 2. 异常处理 ```csharp private async void ProcessDataAsync() { try { await RiskyOperationAsync(); } catch (Exception ex) { MessageBox.Show($"操作失败:{ex.Message}"); } } ``` #### 3. 组合多个异步操作 ```csharp // 并行执行 var task1 = GetDataFromAPIAsync(); var task2 = LoadLocalFileAsync(); await Task.WhenAll(task1, task2); // 顺序执行 var result1 = await Step1Async(); var result2 = await Step2Async(result1); ``` --- ### 四、传统多线程的对比 | 特性 | 传统多线程 | `async/await` | |---------------------|---------------------------|-----------------------------| | 线程资源管理 | 手动创建/销毁 | 线程池自动分配 | | UI线程交互 | 需显式调用Invoke | 自动返回原始上下文[^4] | | 代码可读性 | 回调嵌套复杂 | 线性执行类似同步代码 | | 异常处理 | 需跨线程传递 | 集中式try-catch捕获 | | 取消支持 | 需手动实现 | 内置CancellationToken机制 | --- ### 五、最佳实践建议 1. **避免`async void`**:除事件处理器外,尽量使用`async Task` 2. **合理使用`ConfigureAwait`**:在非UI代码中使用`ConfigureAwait(false)`提升性能 3. **预防死锁**:不要在异步方法中混用`.Wait()`或`.Result` --- ### 进阶示例:带取消功能的异步操作 ```csharp private CancellationTokenSource _cts; private async void StartCancelableTask_Click(object sender, EventArgs e) { _cts = new CancellationTokenSource(); try { var result = await LongRunningOperationAsync(_cts.Token); lblResult.Text = $"结果:{result}"; } catch (OperationCanceledException) { lblResult.Text = "操作已取消"; } } private async Task<int> LongRunningOperationAsync(CancellationToken token) { for (int i = 0; i < 10; i++) { token.ThrowIfCancellationRequested(); await Task.Delay(1000, token); } return DateTime.Now.Second; } private void CancelOperation_Click(object sender, EventArgs e) { _cts?.Cancel(); } ``` ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值