c#Task的使用

        在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());
                }
            }

  • 3
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值