C#中线程的使用(2)--Parallel与Task

一、简介

接着上一章,本次随笔接着记录一下Parallel与Task开启线程的方法

二、Parallel的使用

1. Parallel.Invoke()

可实现并行执行所提供的多个操作,可使用Invoke执行多个Action,由于主线程也会参与,所以若是有耗时的计算,则会出现明显卡顿的现象

Parallel.Invoke(
    () => { Console.WriteLine("hello1"); },
    () => { Console.WriteLine("hello2"); },
    () => { Console.WriteLine("hello3"); },
    () => { Console.WriteLine("hello4"); },
    () => { Console.WriteLine("hello5"); });

上述例子是同时启动了5个线程分别去打印hello

2. Parallel.For 与 Parallel.ForEach

这两个方法都可以开启并行线程,Parallel.For需要传入起始索引和结束索引,并且会将当前索引作为参数传入执行的方法中,Parallel.ForEach 需要传入一个IEnumerable<TSource>,并且会将遍历到元素值传入执行的方法中

Parallel.For(0, 5, t => Console.WriteLine(t);));//打印输出0-4 开启5个线程

List<int> temp = new List<int>() { 1,2,3,45,6 };
Parallel.ForEach(temp, t => Console.WriteLine(t));//打印输出集合中的元素 开启5个线程

3. 控制最大并发数

Parallel可以使用ParallelOptions设置开启线程的数量,最大的并发数为3,当一个线程结束后会立马开启新的线程执行剩余的方法

ParallelOptions parallelOptions = new ParallelOptions();
parallelOptions.MaxDegreeOfParallelism = 3;//设置并发任务的最大数目
Parallel.For(0, 5, parallelOptions, t => Console.WriteLine(t);));
List<int> temp = new List<int>(){1,2,3,4,5,6};
Parallel.ForEach(temp, parallelOptions, t => Console.WriteLine(t));

三、Task

Task 对象通常以异步方式执行在线程池线程,避免了线程的开启和销毁,减小了资源的消耗,我们可以根据IsCanceled, ,IsCompleted, 和 IsFaulted 属性确定任务的状态

1. 创建任务

使用Wait()方法,可以阻塞主线程,直到该任务结束

//方式1
Task task = new Task(() =>Console.WriteLine(Thread.CurrentThread.ManagedThreadId.ToString("00")));
task.Start();//开始任务
//方式2
Task task1 = Task.Run(() => Console.WriteLine(Thread.CurrentThread.ManagedThreadId.ToString("00")));
task1.Wait();//等待task1完成
Console.WriteLine(task1.IsCompleted);//打印是否完成
//方式3 使用工厂创建一个新任务
TaskFactory taskFactory = Task.Factory;
taskFactory.StartNew(() =>Console.WriteLine(Thread.CurrentThread.ManagedThreadId.ToString("00")));

2. 延续任务或回调

2.1 当任务结束时,想在任务完成后执行其他延续任务则可以使用ContinueWith()方法

Task task = Task.Run(() => Console.WriteLine(Thread.CurrentThread.ManagedThreadId.ToString("00")))
    .ContinueWith(t => Console.WriteLine(t.IsCompleted));

2.2 当有多个任务执行时,想要在一个或者所有任务结束后,继续其他任务的,则可以使用ContinueWhenAny() 和 ContinueWhenAll() 延续任务会重新在使用线程池中空闲线程,不会阻塞当前线程

List<Task> tasksList = new List<Task>();
TaskFactory taskFactory = new TaskFactory();
tasksList.Add(Task.Run(() => { Console.WriteLine("启动任务1"); }));//这里只是打印到控制台 可以添加具体的任务
tasksList.Add(Task.Run(() => { Console.WriteLine("启动任务2"); }));
tasksList.Add(Task.Run(() => { Console.WriteLine("启动任务3"); }));
tasksList.Add(Task.Run(() => { Console.WriteLine("启动任务4"); }));
tasksList.Add(Task.Run(() => { Console.WriteLine("启动任务5"); }));
taskFactory.ContinueWhenAny(tasksList.ToArray(), t => {Console.WriteLine($"有一个任务已完成"); });//有一个任务完成时,回执行该延续任务,并且可以拿到当前已完成的任务t
tasksList.Add(taskFactory.ContinueWhenAll(tasksList.ToArray(), t => { Console.WriteLine($"所有任务已完成"); }));//当所有任务都完成时,会进入该延续任务

2.3 若需要主线程等待一个或全部任务结束则需要使用到Task.WaitAny()或Task.WaitAll()

int taskIndex = Task.WaitAny(tasksList.ToArray());//当有任何一个任务完成时,不在阻塞主线程,并返回当前已完成任务的索引
Task.WaitAll(tasksList.ToArray());//所有任务都结束后,主线程才会继续向下执行

2.4 假设我们不知到数据存在哪个数据库或者哪个存储区,需要启用多个任务进行查询,并且在查询到了数据之后立马对拿到的数据进行下一步计算时,就需要需要使用到ContinueWhenAny()来创建延续任务,并且返回一个当前延续性任务的Task

byte[] vals = new byte[4];//存放查询到的数据
List<Task> tasksList = new List<Task>();//任务集合
TaskFactory taskFactory = new TaskFactory();//任务工厂
tasksList.Add(taskFactory.StartNew(() => { WriteHomework(2, vals, 0); }));//添加任务1
tasksList.Add(taskFactory.StartNew(() => { WriteHomework(3, vals, 1); }));//添加任务2
tasksList.Add(taskFactory.StartNew(() => { WriteHomework(1, vals, 2); }));//添加任务3
tasksList.Add(taskFactory.StartNew(() => { WriteHomework(4, vals, 3); }));//添加任务4
taskFactory.ContinueWhenAny(tasksList.ToArray(), t =>
{
    Console.WriteLine($" 当前已完成任务:{tasksList.IndexOf(t)}  获取到数据:{vals[tasksList.IndexOf(t)]}");
});//创建延续任务,打印出获取的值  已完成的任务会作为参数传递到延续性任务中(t)

/// <summary>
/// 模拟获取的数据的方法
/// </summary>
/// <param name="time">执行方法耗时</param>
/// <param name="vals">数据存储区</param>
/// <param name="taskindex">当前任务索引</param>
private void WriteHomework(int time, byte[] vals, int taskindex)
{
    Thread.Sleep(time * 1000);
    vals[taskindex] = (byte)(new Random().Next(255));
}
posted @ 2022-11-19 13:35  just--like  阅读(705)  评论(0编辑  收藏  举报