在本文中,我们展示了如何在C#中使用Task进行并发操作。
并发编程用于两种任务:I/O绑定任务和CPU绑定任务。从网络请求数据、访问数据库或读取和写入都是IO绑定任务。CPU任务是计算量大的任务,例如数学计算或图形处理。
异步操作适用于I/O密集型任务。并行操作适用于受CPU限制的任务。与其他语言不同,Task可用于异步和并行操作。
Task表示并发操作。
Task Task<TResult>
Task代表一个并发操作,而Task代表一个可以返回值的并发操作。
Task.Run方法用于并发运行受CPU限制的代码;最好是并行运行。它将指定的工作排队以在ThreadPool上运行,并返回该工作的任务或Task句柄。
.NET包含许多异步执行I/O绑定代码的方法,例如StreamReader.ReadLineAsync或HttpClient.GetAsync。它们与async/await关键字一起使用。
C#任务.运行
Task.Run方法将任务放在不同的线程上。它适用于CPU密集型任务。
Console.WriteLine($"Main thread {getThreadId()} begin");
Task.Run(() =>
{
Console.WriteLine($"Thread {getThreadId()} begin");
Thread.Sleep(3000);
Console.WriteLine($"Thread {getThreadId()} end");
});
Console.WriteLine($"Main thread {getThreadId()} end");
Console.ReadLine();
int getThreadId()
{
return Thread.CurrentThread.ManagedThreadId;
}
主线程在生成的任务之前完成。为了看到任务完成,我们使用等待用户输入的Console.ReadLine。
$ dotnet run Main thread 1 begin Main thread 1 end Thread 4 begin Thread 4 end
Task表示返回结果的任务。
Task<int> task = Task.Run(() =>
{
Thread.Sleep(3000);
return 2 + 3;
});
var res = await task;
Console.WriteLine(res);
程序展示了如何等待返回计算结果的任务。
C#任务.延迟
Task.Delay创建一个在一段时间延迟后完成的任务。
Console.WriteLine("step 1");
await doTask();
Console.WriteLine("step 2");
async Task doTask()
{
await Task.Delay(3000);
Console.WriteLine("task finished");
}
创建任务的函数必须使用async关键字。
await Task.Delay(3000);
Task.Delay创建一个新任务,它会休眠三秒钟。await运算符等待任务完成。它会阻止主程序的执行,直到任务完成。
$ dotnet run step 1 task finished step 2
C#异步主方法
当我们在Main方法中使用await运算符时,我们必须用async修饰符标记它。
sky main club cotton rocket
这是一个示例文本文件。
namespace AsyncMain;
class Program
{
static async Task Main(string[] args)
{
using StreamReader reader = File.OpenText("words.txt");
string? res = await reader.ReadLineAsync();
Console.WriteLine($"First line is: {res}");
}
}
该示例异步读取文件的第一行。该工作在Main方法中完成。
string? res = await reader.ReadLineAsync();
ReadLineAsync方法返回表示异步读取操作的Task。任务中的结果包含流中的下一行,或者如果已读取所有字符则为空。
$ dotnet run First line is: sky
C#Task.WaitAll
Task.WaitAll方法等待所有提供的任务完成执行。
using System.Diagnostics;
var sw = new Stopwatch();
sw.Start();
Task.WaitAll(f1(), f2(), f3());
sw.Stop();
var elapsed = sw.ElapsedMilliseconds;
Console.WriteLine($"elapsed: {elapsed} ms");
async Task f1()
{
await Task.Delay(4000);
Console.WriteLine("f1 finished");
}
async Task f2()
{
await Task.Delay(7000);
Console.WriteLine("f2 finished");
}
async Task f3()
{
await Task.Delay(2000);
Console.WriteLine("f3 finished");
}
我们测量了三种异步方法的执行时间。
Task.WaitAll(f1(), f2(), f3());
Task.WaitAll等待所有提供的任务完成执行。
$ dotnet run f3 finished f1 finished f2 finished elapsed: 7000 ms
C#任务.ContinueWith
Task.ContinueWith创建一个在目标Task完成时异步执行的延续。
Task<int> task = Task.Run(() =>
runTask()).ContinueWith<int>((x) => x.Result * 2);
var res = await task;
Console.WriteLine(res);
int runTask()
{
int x = 1;
int y = 2;
int z = 3;
Thread.Sleep(1000);
return x + y + z;
}
在示例中,我们使用ContinueWith链接了两个操作。
C#多个异步请求
HttpClient类用于发送HTTP请求和接收来自指定资源的HTTP响应。
var urls = new string[] { "http://webcode.me", "http://example.com",
"http://httpbin.org", "https://ifconfig.me", "http://termbin.com",
"https://github.com"
};
using var client = new HttpClient();
var tasks = new List<Task<HttpResponseMessage>>();
foreach (var url in urls)
{
tasks.Add(client.GetAsync(url));
}
Task.WaitAll(tasks.ToArray());
var data = new List<HttpResponseMessage>();
foreach (var task in tasks)
{
data.Add(await task);
}
foreach (var res in data)
{
Console.WriteLine(res.StatusCode);
}
我们向各种网页发送异步GET请求并获取它们的响应状态代码。
tasks.Add(client.GetAsync(url));
GetAsync向指定的url发送GET请求,并在异步操作中返回响应主体。它返回一个新任务。任务被添加到任务列表中。
Task.WaitAll(tasks.ToArray());
Task.WaitAll等待所有提供的任务完成执行。
data.Add(await task);
await解包操作的结果。
foreach (var res in data)
{
Console.WriteLine(res.StatusCode);
}
我们打印每个请求的状态。
$ dotnet run OK OK OK OK OK OK
在本文中,我们在C#中使用Task进行并发操作。
列出所有C#教程。
