Unity多线程(C#)

作者: 欣羽馨予 | 来源:发表于2015-10-26 17:06 被阅读6654次

前言

在这之前,有很多人在质疑Unity支不支持多线程,事实上Unity是支持多线程的。而提到多线程就要提到Unity非常常用的协程,然而协程并非真正的多线程。协程其实是等某个操作完成之后再执行后面的代码,或者说是控制代码在特定的时机执行。而多线程在Unity渲染和复杂逻辑运算时可以高效的使用多核CPU,帮助程序可以更高效的运行。本篇主要介绍在Unity中如何使用多线程。


  • 首先引入C#中使用多线程的类库
    using System.Threading;

  • 创建线程实例的四种方式
    1.线程执行无参方法

    • 构造语法
      /// <summary>
      /// 初始化 Thread 类的新实例。
      /// </summary>
      /// <param name="start">无参委托对象.</param>
      public Thread(ThreadStart start)
      start
      类型:System.Threading.ThreadStart
      表示开始执行此线程时要调用的方法的 ThreadStart 委托。
    • 实例
      void Start()
      {
      //创建无参线程对象
      Thread thr = new Thread(Func_NoArguments);
      //启动线程
      thr.Start();
      }
      /// <summary>
      /// Function Of No Arguments.
      /// </summary>
      void Func_NoArguments()
      {
      Debug.Log("Run Func_NoArguments");
      }

    2.线程执行有参方法

    • 构造语法
      /// <summary>
      /// 初始化 Thread 类的新实例。
      /// </summary>
      /// <param name="start">有参委托对象.</param>
      public Thread(ParameterizedThreadStart start)
      start
      类型:System.Threading.ParameterizedThreadStart
      一个委托,它表示此线程开始执行时要调用的方法。
      注意:参数只能有一个,且必须为object类型
    • 实例
      void Start()
      {
      //创建有参线程对象
      Thread thr = new Thread(Func_Arguments);
      //启动线程,传入参数
      thr.Start("Lanou");
      }
      /// <summary>
      /// Function Of Have Arguments.
      /// </summary>
      void Func_Arguments(object data)
      {
      Debug.Log("Run Func_Arguments, Data = " + data);
      }

    3.线程执行无参方法,限制线程要使用的最大堆栈大小

    • 构造语法
      /// <summary>
      /// 初始化 Thread 类的新实例。
      /// </summary>
      /// <param name="start">无参委托对象.</param>
      /// <param name="maxStackSize">使用的最大堆栈大小.</param>
      public Thread(ThreadStart start,int maxStackSize)
      start
      类型:System.Threading.ThreadStart
      表示开始执行此线程时要调用的方法的 ThreadStart 委托。
      maxStackSize
      类型:System.Int32
      线程要使用的最大堆栈大小(以字节为单位);如果为 0,则使用可执行文件的文件头中指定的默认最大堆栈大小。
      重要事项:对于部分受信任的代码,如果 maxStackSize 大于默认堆栈大小,则将其忽略。 不引发异常。
    • 实例
      void Start()
      {
      //创建无参线程对象,限制256KB堆栈大小
      Thread thr = new Thread(Func_NoArguments,262144);
      //启动线程
      thr.Start();
      }
      /// <summary>
      /// Function Of No Arguments.
      /// </summary>
      void Func_NoArguments()
      {
      Debug.Log("Run Func_NoArguments");
      }

    4.线程执行有参方法,限制线程要使用的最大堆栈大小

    • 构造语法
      /// <summary>
      /// 初始化 Thread 类的新实例。
      /// </summary>
      /// <param name="start">有参委托对象.</param>
      /// <param name="maxStackSize">使用的最大堆栈大小.</param>
      public Thread(ParameterizedThreadStart start,int maxStackSize)
      start
      类型:System.Threading.ParameterizedThreadStart
      一个委托,它表示此线程开始执行时要调用的方法。
      注意:参数只能有一个,且必须为object类型
      maxStackSize
      类型:System.Int32
      线程要使用的最大堆栈大小(以字节为单位);如果为 0,则使用可执行文件的文件头中指定的默认最大堆栈大小。
      重要事项:对于部分受信任的代码,如果 maxStackSize 大于默认堆栈大小,则将其忽略。 不引发异常。
    • 实例
      void Start()
      {
      //创建有参线程对象,限制256KB堆栈大小
      Thread thr = new Thread(Func_Arguments,262144);
      //启动线程,传入参数
      thr.Start("Lanou");
      }
      /// <summary>
      /// Function Of Have Arguments.
      /// </summary>
      void Func_Arguments(object data)
      {
      Debug.Log("Run Func_Arguments, Data = " + data);
      }
  • 启动线程(上文已使用)

    • 无参启动
      void Start()
      {
      //创建无参线程对象
      Thread thr = new Thread(Func_NoArguments);

//启动线程
thr.Start();

    }
    /// <summary>
    /// Function Of No Arguments.
    /// </summary>
    void Func_NoArguments()
    {
        Debug.Log("Run Func_NoArguments");
    }
  • 有参启动
    void Start()
    {
    //创建有参线程对象
    Thread thr = new Thread(Func_Arguments);

//启动线程,传入参数
thr.Start("Lanou");

    }
    /// <summary>
    /// Function Of Have Arguments.
    /// </summary>
    void Func_Arguments(object data)
    {
        Debug.Log("Run Func_Arguments, Data = " + data);
    }
  • 常用方法
    • public static void Sleep( int millisecondsTimeout)将当前线程挂起指定的毫秒数。
      millisecondsTimeout
      millisecondsTimeout
      类型:System.Int32
      挂起线程的毫秒数。 如果 millisecondsTimeout 参数的值为零,则该线程会将其时间片的剩余部分让给任何已经准备好运行的、有同等优先级的线程。 如果没有其他已经准备好运行的、具有同等优先级的线程,则不会挂起当前线程的执行。
    • public void Resume()
      继续已挂起的线程。(已过时)
    • public void Abort()
      在调用此方法的线程上引发 ThreadAbortException,以开始终止此线程的过程。 调用此方法通常会终止线程。
    • public void Join()
      阻止调用线程直到线程终止,同时继续执行标准的 COM 和 SendMessage 传送。
    • public enum ThreadPriority
      指定 Thread 的调度优先级。
成员名称 描述
AboveNormal 可以将 Thread 安排在具有 Highest 优先级的线程之后,在具有 Normal 优先级的线程之前。
BelowNormal 可以将 Thread 安排在具有 Normal 优先级的线程之后,在具有 Lowest 优先级的线程之前。
Highest 可以将 Thread 安排在具有任何其他优先级的线程之前。
Lowest 可以将 Thread 安排在具有任何其他优先级的线程之后。
Normal 可以将 Thread 安排在具有 AboveNormal 优先级的线程之后,在具有 BelowNormal 优先级的线程之前。 默认情况下,线程具有 Normal 优先级。
  • 通过线程池执行线程

    • ThreadPool.QueueUserWorkItem 方法 (WaitCallback)
      public static bool QueueUserWorkItem(WaitCallback callBack)
      callBack
      类型:System.Threading.WaitCallback
      一个 WaitCallback,表示要执行的方法。
      返回值
      类型:System.Boolean
      如果此方法成功排队,则为 true;如果无法将该工作项排队,则引发 NotSupportedException。
  • Unity使用多线程注意

    1. 变量都是共享的(都能指向相同的内存地址)
    2. UnityEngine的API不能在分线程运行
    3. UnityEngine定义的基本结构(int,float,Struct定义的数据类型)可以在分线程计算,如 Vector3(Struct)可以 , 但Texture2d(class,根父类为Object)不可以。
    4. UnityEngine定义的基本类型的函数可以在分线程运行
  • Unity多线程插件
    LOOM Multi Threading Framework 1.7 下载地址

    LOOM Multi Threading Framework
    • 核心方法
      /// <summary>
      /// Unlike "StartMultithreadedWorkloadExecution", you will have to build your own IThreadWorkerObject.
      /// Downside: It requires some extra work. Upside: you got more controll over what goes in and comes out
      /// Infact: You can create you own polymorphed IThreadWorkerObject-array, each ellement being a completely different type. For example: the statemachines of enemies are IThreadWorkerObject's and the array contains completely different classes with enemies/AI-behaviours.
      /// </summary>
      /// <param name="workerObjects">An array of IThreadWorkerObject objects to be handled by the threads. If you want multiple cores/threads to be active, make sure that the number of IThreadWorkerObject's proves matches/exeeds your preferred number maxWorkingThreads. </param>
      /// <param name="onComplete">Fired when all re-packaged workLoad-objects are finished computing</param>
      /// <param name="onPackageExecuted">Fires foreach finished re-packaged set of workLoad-object</param>
      /// <param name="maxThreads"> Lets you choose how many threads will be run simultaneously by the threadpool. Default: -1 == number of cores minus one, to make sure the MainThread has at least one core to run on. (quadcore == 1 core Mainthread, 3 cores used by the ThreadPoolScheduler)</param>
      /// <param name="scheduler">If Null, a new ThreadPoolScheduler will be instantiated.</param>
      /// <param name="safeMode">Executes all the computations within try-catch events, logging it the message + stacktrace</param>
      /// <returns>A ThreadPoolScheduler that handles all the repackaged workLoad-Objects</returns>
      public static ThreadPoolScheduler StartMultithreadedWorkerObjects(IThreadWorkerObject[] workerObjects, ThreadPoolSchedulerEvent onCompleteCallBack, ThreadedWorkCompleteEvent onPackageExecuted = null, int maxThreads = -1, ThreadPoolScheduler scheduler = null, bool safeMode = true)
      {
      if (scheduler == null)
      scheduler = CreateThreadPoolScheduler();

          scheduler.StartASyncThreads(workerObjects, onCompleteCallBack, onPackageExecuted, maxThreads, safeMode);
          return scheduler;
      }
      

结束语

Unity可以使用多线程,但对其有很多限制,所以在不使用UnityEngine API的情况下,可以使用多线程,提高多核CPU的使用率。通常可以将需要大量计算的算法内容,放置到多线程中执行,包括逻辑框架也可以放到多线程中执行。本篇理论性较强,后期会陆续发布实战型文章。

相关文章

网友评论

  • 2793ca61a200: 最近用c#写了一个dll文件给unity游戏调用,在一个Init函数里面用Thread类实现拉起一个线程,但是unity调用这个Init时,执行了Thread的start,但是这个线程的入口函数却并没有运行,很苦恼,不知道是怎么回事儿
    欣羽馨予:@天涯柯 Unity还是偏爱自己的协程的
    2793ca61a200:自己用控制台窗口程序写的测试代码是可以正常执行的,就是在unity里面测试的时候不行:sweat: 只有找游戏公司继续来调试了
    欣羽馨予:把DLL解开,调试一下吧,是不是线程阻塞了。
  • 我是胡歌:莫老大,线程由系统调度,共享堆,不共享栈。引用类型和值类型的变量都可以共享吗?
    欣羽馨予:这个东西测一下不就出来了吗
  • 97acc43d80e6:我试过多线程,有一个问题是退出游戏后线程仍然不会结束导致CPU占用不能释放。
    我是胡歌:自己开启的线程必须要手动结束。不发布环境下测试的结果是这样
    2bfdc7ef64e6:实验证明,必须要手动结束线程,不然在测试的时候会卡死
    欣羽馨予:@Leshao 发布出来就好啦
  • f1c386c79b59:那个插件的下载地址失效了,这里是最新的,更新一下吧 https://www.assetstore.unity3d.com/en/#!/content/7285
  • paraself:多线程最难得是管理

本文标题:Unity多线程(C#)

本文链接:https://www.haomeiwen.com/subject/kirxhttx.html