C#异步任务使用
C#的异步编程升级过很多次语法,古老的异步使用方式我在之前的博文中写过,现在总结一下新的异步任务 Task/async/await 语法,新的异步编程方式,更简单,更实用,基本省去了使用一大堆回调方法的麻烦,而且也不用在实时维护多线程了,总之异步很强大和简洁,简洁是一种美德。
文章目录
1. Task创建和运行
#region Task 创建和运行
//1.new方式实例化一个Task,需要通过Start方法启动
Task task1 = new Task(() =>
{
Thread.Sleep(100);
Console.WriteLine($"hello, task1的线程ID为{Thread.CurrentThread.ManagedThreadId}");
});
task1.Start();
//2.Task.Factory.StartNew(Action action)创建和启动一个Task
Task task2 = Task.Factory.StartNew(() =>
{
Thread.Sleep(100);
Console.WriteLine($"hello, task2的线程ID为{ Thread.CurrentThread.ManagedThreadId}");
});
//3.Task.Run(Action action)将任务放在线程池队列,返回并启动一个Task
Task task3 = Task.Run(() =>
{
Thread.Sleep(100);
Console.WriteLine($"hello, task3的线程ID为{ Thread.CurrentThread.ManagedThreadId}");
});
Console.WriteLine("执行主线程!");
#endregion
#region Task 泛型实例
1.new方式实例化一个Task,需要通过Start方法启动
Task<string> task4 = new Task<string>(() =>
{
return $"hello, task1的ID为{Thread.CurrentThread.ManagedThreadId}";
});
task4.Start();
2.Task.Factory.StartNew(Func func)创建和启动一个Task
Task<string> task5 = Task.Factory.StartNew<string>(() =>
{
return $"hello, task2的ID为{ Thread.CurrentThread.ManagedThreadId}";
});
3.Task.Run(Func func)将任务放在线程池队列,返回并启动一个Task
Task<string> task6 = Task.Run<string>(() =>
{
return $"hello, task3的ID为{ Thread.CurrentThread.ManagedThreadId}";
});
Console.WriteLine("执行主线程!");
Console.WriteLine(task4.Result);
Console.WriteLine(task5.Result);
Console.WriteLine(task6.Result);
#endregion
2. 同步执行Task任务
//同步执行,task会阻塞主线程
task1.RunSynchronously();
Console.WriteLine("执行主线程结束!");
3. Task的阻塞方法
Wait,WaitAll,WaitAny
//阻塞主线程。task1,task2都执行完毕再执行主线程
//执行【task1.Wait();task2.Wait();】可以实现相同功能
Task.WaitAll(new Task[] { task1, task2 });
Console.WriteLine("主线程执行完毕!");
4. Task的延续操作
WhenAny,WhenAll,ContinueWith
Task.WhenAll(task1, task2).ContinueWith((t) => {
Thread.Sleep(100);
Console.WriteLine("执行后续操作完毕!");
});
//通过TaskFactroy实现
Task.Factory.ContinueWhenAll(new Task[] { task1, task2 }, (t) =>
{
Thread.Sleep(100);
Console.WriteLine("执行后续操作");
});
5. Task的任务取消
// Task的任务取消
CancellationTokenSource source = new CancellationTokenSource();
int index = 0;
//开启一个task执行任务
Task task7 = new Task(() =>
{
while (!source.IsCancellationRequested)
{
Thread.Sleep(1000);
Console.WriteLine($"第{++index}次执行,线程运行中...");
}
});
task7.Start();
//五秒后取消任务执行
Thread.Sleep(5000);
//source.Cancel()方法请求取消任务,IsCancellationRequested会变成true
source.Cancel();
/*
CancellationTokenSource的功能不仅仅是取消任务执行,
我们可以使用 source.CancelAfter(5000) 实现5秒后自动取消任务,
也可以通过 source.Token.Register(Action action) 注册取消任务触发的回调函数,
即任务被取消时注册的action会被执行
*/
CancellationTokenSource source1 = new CancellationTokenSource();
//注册任务取消的事件
source.Token.Register(() =>
{
Console.WriteLine("任务被取消后执行xx操作!");
});
int index1 = 0;
//开启一个task执行任务
Task task8 = new Task(() =>
{
while (!source.IsCancellationRequested)
{
Thread.Sleep(1000);
Console.WriteLine($"第{++index1}次执行,线程运行中...");
}
});
task8.Start();
//延时取消,效果等同于Thread.Sleep(5000);source.Cancel();
source1.CancelAfter(5000);
6. 异步方法(async/await)
//异步方法(async/await)
string content = GetContentAsync(Environment.CurrentDirectory + @"/test.txt").Result;
//调用同步方法
//string content = GetContent(Environment.CurrentDirectory + @"/test.txt");
Console.WriteLine(content);
//异步读取文件内容
async static Task<string> GetContentAsync(string filename)
{
FileStream fs = new FileStream(filename, FileMode.Open);
var bytes = new byte[fs.Length];
//ReadAync方法异步读取内容,不阻塞线程
Console.WriteLine("开始读取文件");
int len = await fs.ReadAsync(bytes, 0, bytes.Length);
string result = Encoding.UTF8.GetString(bytes);
return result;
}
//同步读取文件内容
static string GetContent(string filename)
{
FileStream fs = new FileStream(filename, FileMode.Open);
var bytes = new byte[fs.Length];
//Read方法同步读取内容,阻塞线程
int len = fs.Read(bytes, 0, bytes.Length);
string result = Encoding.UTF8.GetString(bytes);
return result;
}
7. 异步综合实例
// 异步方法实例
Task t = RunProgram();
t.Wait();
Console.ReadKey();
//测试函数
static async Task RunProgram()
{
var taskQueue = new ConcurrentQueue<CustomTask>();
var cts = new CancellationTokenSource();
//生成任务添加至并发队列
var taskSource = Task.Run(() => TaskProducer(taskQueue));
//同时启动四个任务处理队列中的任务
Task[] processors = new Task[4];
for (int i = 1; i <= 4; i++)
{
string processId = i.ToString();
processors[i - 1] = Task.Run(
() => TaskProcessor(taskQueue, "Processor " + processId, cts.Token)
);
}
await taskSource;
//向任务发送取消信号
cts.CancelAfter(TimeSpan.FromSeconds(2));
await Task.WhenAll(processors);
}
//产生任务
static async Task TaskProducer(ConcurrentQueue<CustomTask> queue)
{
for (int i = 0; i < 20; i++)
{
await Task.Delay(50);
var workItem = new CustomTask { Id = i };
queue.Enqueue(workItem);
Console.WriteLine("task {0} has been posted", workItem.Id);
}
}
//执行任务
static async Task TaskProcessor(ConcurrentQueue<CustomTask> queue, string name, CancellationToken token)
{
CustomTask workItem;
bool dequeueSuccesful = false;
await GetRandomDelay();
do
{
dequeueSuccesful = queue.TryDequeue(out workItem);
if (dequeueSuccesful)
{
Console.WriteLine("task {0} has been processed by {1}", workItem.Id, name);
}
await GetRandomDelay();
}
while (!token.IsCancellationRequested);
}
static Task GetRandomDelay()
{
int delay = new Random(DateTime.Now.Millisecond).Next(1500);
return Task.Delay(delay);
}
class CustomTask
{
public int Id { get; set; }
}