在donet线程的线程发展过程中,在donet1.0的时候(Thread),微软为线程提供了大量很犀利的接口,如果用户对这些接口使用不好的话,会出现很严重的问题,因为线程不是c#所拥有的的,而是OS拥有的,所以在c#端操作线程的结束挂起...是不好的。所以微软提供的大量接口对于操作线程并不是很友好。
到了donet2.0的时候微软推出了线程池,线程池中并没有Thread里面那些犀利的接口,但是同时线程的可操作性就得需要自己去写,自己去封装。
到了donet3.0的时候,因为基于2.0的线程池接口非常少,所以在这一时期,微软使用Task来开启启动线程,Task里面有提供了很多操作线程的接口
Task是基于ThreadPool封装的
一.Task创建线程的几种方式
// 第一种方式
Task.Run(() => {DoSomething("TaskRun1");});
// 第二种方式
new Task(() => { DoSomethingLong("另一种方式使用Task.Run"); }).Start();
// 第三种方式
TaskFactory tf = Task.Factory;// donet4.0出现的,效果和task一样
tf.StartNew(() => { DoSomethingLong("使用TaskFactory"); });// 功能和Taks.Run一样
二.Task里的WaitAll和WaitAny
wait会卡界面(同步形式)
WaitAll代表等待所有的线程完成之后,才会有后序动作
WaitAny代表等待某一个线程完成之后,就会有后续动作
List<Task> taskList = new List<Task>();
taskList.Add(Task.Run(() => { this.Coding("张三", "Client"); }));
taskList.Add(Task.Run(() => { this.Coding("李四", "Portal"); }));
taskList.Add(Task.Run(() => { this.Coding("王五", "Service"); }));
taskList.Add(Task.Run(() => { this.Coding("李六", "Jump"); }));
Task.WaitAny(taskList.ToArray(),1000);
Task.WaitAny(taskList.ToArray());// 会阻塞当前线程,等某个任务完成后,才进行下一个,卡界面
Task.WaitAll(taskList.ToArray());
Task.WaitAll(taskList.ToArray(), 1000);// 表示会等待所有的线程1s,但是只会等待1s
三.Task里的WhenAll和WhenAny
when不会卡界面(异步形式)
WhenAll代表等待所有的线程完成之后,才会有后序动作
WaitAny代表等待某一个线程完成之后,就会有后续动作
List<Task> taskList = new List<Task>();
taskList.Add(Task.Run(() => { this.Coding("张三", "Client"); }));
taskList.Add(Task.Run(() => { this.Coding("李四", "Portal"); }));
taskList.Add(Task.Run(() => { this.Coding("王五", "Service"); }));
taskList.Add(Task.Run(() => { this.Coding("李六", "Jump"); }));
// 当Task.WhenAny(taskList.ToArray())完成之后会返回一个新的Task
// Task.ContinueWith就是在Task.WhenAny(taskList.ToArray())结束后在开启一个后序的线程去做其他的事情
Task.WhenAny(taskList.ToArray()).ContinueWith(t =>
{
this.DoSomething("某个线程执行完成");
})
Task.WhenAll(taskList.ToArray()).ContinueWith(t =>
{
this.DoSomething("所有线程执行完成");
})
// 使用TaskFactory的方式实现这两个接口
List<Task> taskList = new List<Task>();
TaskFactory taskFactory = new TaskFactory();
taskList.Add(taskFactory.StartNew(o => { this.Coding("张三", "Client"); }, "张三"));// 第二个参数是附加信息,task没有taskFactory有第二个参数
taskList.Add(taskFactory.StartNew(o => { this.Coding("李四", "Portal"); }, "李四"));
taskList.Add(taskFactory.StartNew(o => { this.Coding("王五", "Service"); }, "王五"));
taskList.Add(taskFactory.StartNew(o => { this.Coding("李六", "Jump"); }, "李六"));
taskList.Add(taskFactory.StartNew(o => { this.Coding("麻子", "Monitor"); }, "麻子"));
// t是某一个任务完成后返回的数据
taskFactory.ContinueWhenAny(taskList.ToArray(), t =>
{
Console.WriteLine(t.AsyncState);
Console.WriteLine("我是第一个完成的" +Thread.CurrentThread.ManagedThreadId.ToString());
});
taskFactory.ContinueWhenAll(taskList.ToArray(), tList =>
{
Console.WriteLine(tList[0].AsyncState);
Console.WriteLine("部署环境,联调测试..." + Thread.CurrentThread.ManagedThreadId.ToString());
});
四.如果希望使用WaitAll又不卡界面,我的一个解决方案就是在拿出一个线程包裹起来他
// 希望既可以等待又可以不卡界面,再用一个线程去包裹这些线程
Task.Run(() => {
Task.WaitAny(taskList.ToArray());
Task.WaitAll(taskList.ToArray())
});
五.控制Task的线程数量
方法一:控制全局的线程池中线程的数量(因为Task是基于线程池进行的封装)
方法二: 使用List集合搭配WaitAny配合使用控制线程的数量
// 方法1
{
ThreadPool.SetMaxThreads(12, 12);// 控制最大线程数量(可以控制Task的) 这种方法不好
// 因为ThreadPool是全局的,这样设置是设置了全局的线程池最大是12,在其他线程使用线程池的时候,最大数量也是12
for (int i = 0; i < 100; i++)
{
Task.Run(() =>
{
DoSomething();
});
}
}
// 方法2
{
// Task控制线程数量并不是很好
List<int> list = new List<int>();
for (int i = 0; i < 10000; i++)
{
list.Add(i);
}
// 接收一个int类型的参数
Action<int> action = i =>
{
DoSomething("");
Thread.Sleep(200);
};
List<Task> taskList = new List<Task>();
// 完成10000个任务 但是最多11个线程运行
foreach (var item in list)
{
int k = item;
taskList.Add( Task.Run(() => {
action.Invoke(k);// 不能直接使用item,可以自己思考下为什么
}));
if (taskList.Count > 10)
{
// 等待某一个线程完成之后
Task.WaitAny(taskList.ToArray());
// 清理线程集合,把未完成线程先留着,然后再去foreach
// 这里会得到一个全新的List
taskList = taskList.Where(t =>
t.Status != TaskStatus.RanToCompletion).ToList();
}
Task.WaitAll(taskList.ToArray());
}
}