C# 异步操作汇总

在C#中,异步操作(Asynchronous Operations)可以提高程序的性能和响应能力。通常情况下,程序会等待某个操作完成之后才会继续执行下一个操作,这会导致程序的运行速度变慢。而异步操作可以让程序在等待某个操作完成的同时,执行其他操作,从而提高程序的运行效率。

在C#中,实现异步操作的方式有以下几种:

1.使用异步方法

C# 5.0引入了异步方法(Async Methods)的概念,使得编写异步代码变得更加容易。异步方法使用async关键字标记,返回类型必须是Task或Task,方法中使用await关键字来等待异步操作完成。

以下是一个使用异步方法实现异步操作的示例:

csharp 复制代码
public async Task<int> DownloadFileAsync(string url, string savePath)
{
    using (var httpClient = new HttpClient())
    {
        var response = await httpClient.GetAsync(url);
        using (var fileStream = new FileStream(savePath, FileMode.Create))
        {
            await response.Content.CopyToAsync(fileStream);
        }
    }
    return 0;
}

在上面的示例中,DownloadFileAsync方法使用async关键字标记,返回类型是Task。方法中使用await关键字等待HttpClient.GetAsync和Stream.CopyToAsync方法完成异步操作。

2.使用Task.Run方法

Task.Run方法可以在新的线程上执行代码,因此也可以用来实现异步操作。使用Task.Run方法需要传入一个委托,该委托中的代码将在新的线程上执行。

以下是一个使用Task.Run方法实现异步操作的示例:

csharp 复制代码
public async Task<int> DownloadFileAsync(string url, string savePath)
{
    await Task.Run(() =>
    {
        using (var httpClient = new HttpClient())
        {
            var response = httpClient.GetAsync(url).Result;
            using (var fileStream = new FileStream(savePath, FileMode.Create))
            {
                response.Content.CopyToAsync(fileStream).Wait();
            }
        }
    });
    return 0;
}

在上面的示例中,DownloadFileAsync方法使用await关键字等待Task.Run方法执行的委托完成异步操作。

3.使用TaskCompletionSource类

TaskCompletionSource类可以用来创建一个可以异步完成的任务,然后通过SetResult或SetException方法来完成任务。使用TaskCompletionSource类需要手动编写异步代码。

以下是一个使用TaskCompletionSource类实现异步操作的示例:

csharp 复制代码
public static Task<string> GetResultAsync()
{
    var tcs = new TaskCompletionSource<string>();
    SomeMethod(result => tcs.SetResult(result));
    return tcs.Task;
}

这个示例的作用是,异步获取一个字符串结果。它使用了 TaskCompletionSource 类来创建一个 Task 对象,并在回调方法中将结果传递给该对象。

以下是重写后的示例,更加简洁易懂:

csharp 复制代码
public static async Task<string> GetResultAsync()
{
    return await Task.Run(() => SomeMethod());
}

在这个示例中,使用了 Task.Run 方法来将 SomeMethod 方法包装成一个 Task 对象,并通过 await 关键字来等待该对象的完成。

相比于使用 TaskCompletionSource 类来手动管理异步操作,使用 Task.Run 方法和 await 关键字更加简洁和易懂。同时,它也能够充分利用 .NET Framework 4.5 引入的异步编程模型,更好地利用系统资源,提高程序的性能和响应速度。

4.使用async和await异步编程

async和await是.NET Framework 4.5引入的一种新的异步编程模型,它基于Task和Task,使异步编程更加简单和直观。使用async和await可以让程序员专注于异步操作的逻辑,而不是繁琐的状态管理和线程调度。

以下是使用async和await编写一个简单异步操作的示例:

csharp 复制代码
public async Task<int> AsyncMethodAsync(int arg)
{
    int result = await Task.Run(() =>
    {
        return arg * 2; // 异步操作
    });
 
    return result; // 返回异步操作的结果
}

5.使用Parallel类进行并行编程

Parallel类是.NET Framework提供的一种用于执行并行操作的工具类,它提供了一些方法,可以让程序员轻松地编写并行操作,以提高程序的性能和效率。

以下是使用Parallel类执行并行操作的示例:

csharp 复制代码
public int[] ParallelMethod(int[] arr)
{
    Parallel.For(0, arr.Length, i =>
    {
        arr[i] = arr[i] * 2; // 并行操作
    });
 
    return arr; // 返回并行操作的结果
}

区别:

这几种异步编程方法都可以实现异步操作,但它们之间存在一些差异:

BeginInvoke/EndInvoke方式是.NET Framework较早期的异步编程模型,适用于.NET Framework 1.1和2.0版本,它需要使用委托和回调函数进行异步操作的管理和完成。但是它比较繁琐,难以理解和维护,因此已经逐渐被Task和async/await方式所取代。

Task和Task方式是.NET Framework 4.0引入的一种新的异步编程模型,它更加灵活和直观,可以方便地管理和控制异步操作的状态和结果。使用Task和Task可以轻松地实现异步操作的取消、

6.通过事件(Event)异步调用

使用事件机制也是一种实现异步编程的方式。这种方式的核心思想是,调用者注册一个事件处理程序,然后异步操作执行完毕时,会调用该事件处理程序并传递操作结果。

以下是使用事件实现异步编程的示例代码:

csharp 复制代码
public class AsyncOperation
{
    public event EventHandler Completed;
 
    public void Start()
    {
        // 模拟异步操作
        Task.Delay(1000).ContinueWith(task => {
            OnCompleted(new EventArgs());
        });
    }
 
    protected virtual void OnCompleted(EventArgs e)
    {
        Completed?.Invoke(this, e);
    }
}
csharp 复制代码
// 调用异步操作
var operation = new AsyncOperation();
operation.Completed += (sender, e) => {
    Console.WriteLine("异步操作完成!");
};
operation.Start();

7.使用异步委托(Async delegate)

使用异步委托也是一种实现异步编程的方式。异步委托是指一个返回类型为 Task 或 Task 的委托,可以使用 async 和 await 关键字来异步调用。

以下是使用异步委托实现异步编程的示例代码:

csharp 复制代码
public class AsyncOperation
{
    public async Task<int> StartAsync()
    {
        // 模拟异步操作
        await Task.Delay(1000);
        return 42;
    }
}
// 调用异步操作
var operation = new AsyncOperation();
var result = await operation.StartAsync();
Console.WriteLine($"异步操作完成,结果为:{result}");

使用异步委托的好处是可以在调用方使用 await 关键字来等待异步操作完成,并且可以直接获取异步操作的结果。

8.使用异步的 LINQ(LINQ with async)

LINQ(Language Integrated Query)是 C# 的一种语言特性,可以方便地进行数据查询和转换操作。在 .NET Framework 4.5 中,引入了一些新的异步操作符,使得 LINQ 查询可以以异步方式进行。

以下是使用异步的 LINQ 实现异步编程的示例代码:

csharp 复制代码
var numbers = Enumerable.Range(1, 10);
 
// 异步筛选出偶数
var evenNumbers = await Task.Run(() => numbers.Where(n => n % 2 == 0));
 
Console.WriteLine("筛选出的偶数为:");
foreach (var number in evenNumbers)
{
    Console.WriteLine(number);
}

使用异步的 LINQ 可以简化代码,并且可以在查询操作比较耗时的情况下提高程序的性能。

区别和选择

上面介绍了几种常用的异步编程方式,每种方式都有自己的优缺点,适用于不同的场景。下面是它们之间的一些区别和选择:

通过委托实现异步编程,适用于简单的异步操作,调用方只需要等待异步操作完成即可,不需要对结果进行处理。

使用 Task 或 Task 类。Task 和 Task 是 .NET 框架中的一部分,是异步编程的基本构建块。它们可以用于创建异步操作、处理异步结果和执行连续异步操作。

Task 是一个代表异步操作的类,它没有返回值。Task 是一个代表异步操作的类,它返回一个 T 类型的值。使用 Task 或 Task 可以很方便地执行异步操作,因为它们可以与 async 和 await 关键字一起使用,从而使异步代码看起来像同步代码。

以下是使用 Task 和 Task 的示例:

csharp 复制代码
// 使用 Task 执行异步操作
public async Task DoAsyncOperation()
{
    await Task.Run(() =>
    {
        // 异步操作代码
    });
}
 
// 使用 Task<T> 执行异步操作并返回结果
public async Task<string> DoAsyncOperationWithResult()
{
    var result = await Task.Run(() =>
    {
        // 异步操作代码
        return "result";
    });
 
    return result;
}

在上面的示例中,DoAsyncOperation 和 DoAsyncOperationWithResult 方法都使用 Task 或 Task 类来执行异步操作。它们使用 await 关键字等待异步操作完成,然后返回结果(如果有)。

Task 和 Task 的区别在于 Task 可以返回一个值,而 Task 不可以。另外,Task 和 Task 之间的其他区别与使用 async/await 关键字的异步方法和异步 Lambda 表达式的区别类似。在执行异步操作时,Task.Run 方法是最常用的方法之一,因为它允许您在一个新的线程上执行操作。

csharp 复制代码
await Task.Run(() =>
{
    // 异步操作代码
});

上面的代码将在一个新的线程上执行异步操作。在这种情况下,Task.Run 返回一个 Task 对象,该对象代表异步操作。由于使用了 async 和 await 关键字,所以可以等待异步操作完成,然后继续执行其他代码。

使用 Task 或 Task 的主要优点是,它们提供了一种更灵活的方式来执行异步操作,因为它们允许您在异步操作完成之前执行其他代码。此外,Task.Run 方法可以让您在单独的线程上执行操作,这使得异步编程更容易。但是,由于 Task.Run 创建了新的线程,所以使用 Task.Run 可能会增加应用程序的负载。因此,应该根据具体情况谨慎使用 Task.Run。

相关推荐
小吴同学·1 小时前
.NET6 WebApi第1讲:VSCode开发.NET项目、区别.NET5框架【两个框架启动流程详解】
c#·.netcore·.net core
bluefox19796 小时前
使用 Oracle.DataAccess.Client 驱动 和 OleDB 调用Oracle 函数的区别
开发语言·c#
鲤籽鲲7 小时前
C# MethodTimer.Fody 使用详解
开发语言·c#·mfc
工业3D_大熊8 小时前
3D可视化引擎HOOPS Luminate场景图详解:形状的创建、销毁与管理
java·c++·3d·docker·c#·制造·数据可视化
yngsqq8 小时前
c#使用高版本8.0步骤
java·前端·c#
hccee11 小时前
C# IO文件操作
开发语言·c#
广煜永不挂科13 小时前
Devexpress.Dashboard的调用二义性
c#·express
初九之潜龙勿用15 小时前
C#校验画布签名图片是否为空白
开发语言·ui·c#·.net
吾与谁归in16 小时前
【C#设计模式(13)——代理模式(Proxy Pattern)】
设计模式·c#·代理模式
吾与谁归in16 小时前
【C#设计模式(14)——责任链模式( Chain-of-responsibility Pattern)】
设计模式·c#·责任链模式