小鸟游星野
小鸟游星野
Published on 2025-05-13 / 7 Visits
0
0

Unity Task封装线程池类

using System.Collections;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using UnityEngine;

public class Lesson5 : MonoBehaviour
{
    private bool isRuning = true;

    Task<int> t1;
    Task<string> t2;
    Task<float> t3;

    CancellationTokenSource c;
    // Start is called before the first frame update
    void Start()
    {
        #region 知识点一 认识Task
        //命名空间:System.Threading.Tasks
        //类名:Task
        //Task顾名思义就是任务的意思
        //Task是在线程池基础上进行的改进,它拥有线程池的优点,同时解决了使用线程池不易控制的弊端
        //它是基于线程池的优点对线程的封装,可以让我们更方便高效的进行多线程开发

        //简单理解:
        //Task的本质是对线程Thread的封装,它的创建遵循线程池的优点,并且可以更方便的让我们控制线程
        //一个Task对象就是一个线程
        #endregion

        #region 知识点二 创建无返回值Task的三种方式
        //1.通过new一个Task对象传入委托函数并启动
        //Task t1 = new Task(() =>
        //{
        //    int i = 0;
        //    while (isRuning)
        //    {
        //        print("方式一:" + i);
        //        ++i;
        //        Thread.Sleep(1000);
        //    }
        //});
        //t1.Start();

        //2.通过Task中的Run静态方法传入委托函数
        //Task t2 = Task.Run(() =>
        //{
        //    int i = 0;
        //    while (isRuning)
        //    {
        //        print("方式二:" + i);
        //        ++i;
        //        Thread.Sleep(1000);
        //    }
        //});

        //3.通过Task.Factory中的StartNew静态方法传入委托函数
        //Task t3 = Task.Factory.StartNew(() =>
        //{
        //    int i = 0;
        //    while (isRuning)
        //    {
        //        print("方式三:" + i);
        //        ++i;
        //        Thread.Sleep(1000);
        //    }
        //});

        #endregion

        #region 知识点三 创建有返回值的Task
        //1.通过new一个Task对象闯入委托函数并启动
        //t1 = new Task<int>(() =>
        //{
        //    int i = 0;
        //    while (isRuning)
        //    {
        //        print("方式一:" + i);
        //        ++i;
        //        Thread.Sleep(1000);
        //    }
        //    return 1;
        //});
        //t1.Start();

        ////2.通过Task中的Run静态方法传入委托函数
        //t2 = Task.Run<string>(() =>
        //{
        //    int i = 0;
        //    while (isRuning)
        //    {
        //        print("方式二:" + i);
        //        ++i;
        //        Thread.Sleep(1000);
        //    }
        //    return "1231";
        //});

        ////3.通过Task.Factory中的StartNew静态方法传入委托函数
        //t3 = Task.Factory.StartNew<float>(() =>
        //{
        //    int i = 0;
        //    while (isRuning)
        //    {
        //        print("方式三:" + i);
        //        ++i;
        //        Thread.Sleep(1000);
        //    }
        //    return 4.5f;
        //});


        ////获取返回值
        ////注意:
        ////Resut获取结果时会阻塞线程
        ////即如果task没有执行完成
        ////会等待task执行完成获取到Result
        ////然后再执行后边的代码,也就是说 执行到这句代码时 由于我们的Task中是死循环 
        ////所以主线程就会被卡死
        ////print(t1.Result);
        ////print(t2.Result);
        ////print(t3.Result);

        //print("主线程执行");
        #endregion

        #region 知识点四 同步执行Task
        //刚才我们举的例子都是通过多线程异步执行的
        //如果你希望Task能够同步执行
        //只需要调用Task对象中的RunSynchronously方法
        //注意:需要使用 new Task对象的方式,因为Run和StartNew在创建时就会启动

        //Task t = new Task(()=> {
        //    Thread.Sleep(1000);
        //    print("哈哈哈");
        //});
        ////t.Start();
        //t.RunSynchronously();
        //print("主线程执行");
        //不Start 而是 RunSynchronously
        #endregion

        #region 知识点五 Task中线程阻塞的方式(任务阻塞)
        //1.Wait方法:等待任务执行完毕,再执行后面的内容
        Task t1 = Task.Run(() =>
        {
            for (int i = 0; i < 5; i++)
            {
                print("t1:" + i);
            }
        });

        Task t2 = Task.Run(() =>
        {
            for (int i = 0; i < 20; i++)
            {
                print("t2:" + i);
            }
        });
        //t2.Wait();

        //2.WaitAny静态方法:传入任务中任意一个任务结束就继续执行
        //Task.WaitAny(t1, t2);

        //3.WaitAll静态方法:任务列表中所有任务执行结束就继续执行
        //Task.WaitAll(t1, t2);

        //print("主线程执行");
        #endregion

        #region 知识点六 Task完成后继续其它Task(任务延续)
        //1.WhenAll静态方法 + ContinueWith方法:传入任务完毕后再执行某任务
        //Task.WhenAll(t1, t2).ContinueWith((t) =>
        //{
        //    print("一个新的任务开始了");
        //    int i = 0;
        //    while (isRuning)
        //    {
        //        print(i);
        //        ++i;
        //        Thread.Sleep(1000);
        //    }
        //});

        //Task.Factory.ContinueWhenAll(new Task[] { t1, t2 }, (t) =>
        //{
        //    print("一个新的任务开始了");
        //    int i = 0;
        //    while (isRuning)
        //    {
        //        print(i);
        //        ++i;
        //        Thread.Sleep(1000);
        //    }
        //});

        //2.WhenAny静态方法 + ContinueWith方法:传入任务只要有一个执行完毕后再执行某任务
        //Task.WhenAny(t1, t2).ContinueWith((t) =>
        //{
        //    print("一个新的任务开始了");
        //    int i = 0;
        //    while (isRuning)
        //    {
        //        print(i);
        //        ++i;
        //        Thread.Sleep(1000);
        //    }
        //});

        //Task.Factory.ContinueWhenAny(new Task[] { t1, t2 }, (t) =>
        //{
        //    print("一个新的任务开始了");
        //    int i = 0;
        //    while (isRuning)
        //    {
        //        print(i);
        //        ++i;
        //        Thread.Sleep(1000);
        //    }
        //});
        #endregion

        #region 知识点七 取消Task执行
        //方法一:通过加入bool标识 控制线程内死循环的结束

        //方法二:通过CancellationTokenSource取消标识源类 来控制
        //CancellationTokenSource对象可以达到延迟取消、取消回调等功能

        c = new CancellationTokenSource();
        //延迟取消
        c.CancelAfter(5000);
        //取消回调
        c.Token.Register(() =>
        {
            print("任务取消了");
        });
        Task.Run(() =>
        {
            int i = 0;
            while (!c.IsCancellationRequested)
            {
                print("计时:" + i);
                ++i;
                Thread.Sleep(1000);
            }
        });

        //延迟取消

        #endregion

        #region 总结
        //1.Task类是基于Thread的封装
        //2.Task类可以有返回值,Thread没有返回值
        //3.Task类可以执行后续操作,Thread没有这个功能
        //4.Task可以更加方便的取消任务,Thread相对更加单一
        //5.Task具备ThreadPool线程池的优点,更节约性能
        #endregion
    }

    private void OnDestroy()
    {
        isRuning = false;
    }

    // Update is called once per frame
    void Update()
    {
        if(Input.GetKeyDown(KeyCode.Space))
        {
            //isRuning = false;
            //print(t1.Result);
            //print(t2.Result);
            //print(t3.Result);
            c.Cancel();
        }
    }
}


Comment