kizumi_header_banner_img

Hello! Beautiful Kizumi!

加载中

文章导读

Unity进阶之C#知识补充


avatar
Yuyas 2025年12月19日 14

Unity跨平台原理

Project Setting -> Player -> Other Setting ->Scripting Backend

Mono:

Unity使用各种语言进行逻辑实现,在发布时会被编译成IL中间代码,最终这些中间代码在对应的操作系统上通过Mono VM虚拟机翻译成机器码来运行

IL2CPP:

编译成IL中间代码过后,IL2CPP会将其转化成C++代码,再通过个平台的C++编译器直接编译成可执行的原生汇编代码。会将没有引用的类型自动裁剪掉

ThreadPool线程池

开发中频繁创建删除线程会带来性能消耗,产生内存垃圾,为避免这种开销,出现了ThreadPool

作用:

ThreadPool中有若干数量的线程,如果有任务需要处理时,会从线程池中获取一个空闲的线程来执行任务 任务执行完毕后线程不会销毁,而是被线程池回收以供后续任务使用 当线程池中所有的线程都在忙碌时,又有新任务要处理时,线程池才会新建一个线程来处理该任务, 如果线程数量达到设置的最大值,任务会排队,等待其他任务释放线程后再执行 线程池能减少线程的创建,节省开销,可以减少GC垃圾回收的触发

优缺点:

线程池相当于就是一个专门装线程的缓存池(Unity小框架套课中有对缓存池的详细讲解) 优点:节省开销,减少线程的创建,进而有效减少GC触发 缺点:不能控制线程池中线程的执行顺序,也不能获取线程池内线程取消 / 异常 / 完成的通知

相关API:

1.获取可用的工作线程数和I/O线程数

ThreadPool.GetAvailableThreads(out num1, out num2);

2.获取线程池中工作线程的最大/最小数目和I/O线程的最大数目

ThreadPool.GetMaxThreads(out num1, out num2);

3.设置线程池中可以同时处于活动状态的 工作线程的最大/最小数目和I / O线程的最大数目

大于次数的请求将保持排队状态,知直到线程池线程变为可用

更改成功返回true,失败返回false

ThreadPool.SetMaxThreads(20, 20)

4.将方法排入队列以便执行,当线程池中线程变得可用时执行

ThreadPool.QueueUserWorkItem((obj) => {print(obj); print(“开启了一个线程”); }, “123”);

其中第二参数的为obj的值

Task

Task的底层是线程Thread,它的创建遵循线程池的优点,提供了很多线程管理方法,可以更方便的让我们控制线程

创建无返回值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);
  }
});

创建有返回值Task:

只需在以上方法的基础上加上泛型即可,返回结果由t1.Result接收

注意此过程若t1线程未结束,则会阻塞主线程至线程结束返回值为止

同步执行Task

t.RunSynchronously();

这里只能使用Task t = new Task(),因为另外两个会在初始化的时候就立即执行

使用后会阻塞主线程,直到该线程执行完毕

主动阻塞Task(任务阻塞)

1.Wait方法:等待任务执行完毕,再执行后面的内容 t2.Wait();

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

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

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 },Func)

2.WhenAny静态方法 + ContinueWith方法:传入任务只要有一个执行完毕后再执行某任务

Task.WhenAny(t1, t2).ContinueWith(Func);

或 Task.Factory.ContinueWhenAny(new Task[] { t1, t2 }, Func)

异步方法async和await

异步并非多线程,甚至在单线程上都能完成,Unity的协程就是一种异步

async用于修饰函数

await写在async函数内部

使用async修饰异步方法 1.在异步方法中使用await关键字(不使用编译器会给出警告但不报错),否则异步方法会以同步方式执行 2.异步方法名称建议以Async结尾 3.异步方法的返回值只能是void、Task、Task<> 4.异步方法中不能声明使用ref或out关键字修饰的变量

当程序执行到await时,当前线程会立即释放,异步执行后面的任务,

若后面还有await,则主线程又会回归到当前函数直到遇到下一个await

示例

public async void CalcPathAsync(GameObject obj, Vector3 endPos)
{
   print("开始处理寻路逻辑");
   int value = 10;
   await Task.Run(() =>
  {
       //处理复杂逻辑计算 我这是通过 休眠来模拟 计算的复杂性
       Thread.Sleep(1000);
       value = 50;
       //是多线程 意味着我们不能在 多线程里 去访问 Unity主线程场景中的对象
       //这样写会报错
       //print(obj.transform.position);
  });

   print("寻路计算完毕 处理逻辑" + value);
   obj.transform.position = Vector3.zero;
}

注意:Unity中大部分异步方法是不支持异步关键字async和await的,我们只有使用协同程序进行使用 虽然官方 不支持 但是 存在第三方的工具(插件)可以让Unity内部的一些异步加载的方法 支持 异步关键字 https://github.com/svermeulen/Unity3dAsyncAwaitUtil

静态导入 using static

例如:

Mathf.Max(a,b);

其中Max是Mathf中的静态方法

Mathf在UnityEngine命名空间中

using static UnityEngine.Mathf;

可以直接使用Max

异常筛选器when,nameof…….

字面值改进:int i = 1_9547_7895;下划线不影响程序

out新用法:Func(out int a,out int b);可以在参数里初始化(out相当于c++&引用传递)

弃元符号_:Func(out int a,out _);放弃第二个参数的传递

ref修饰临时变量和返回值

ref用法回顾:

int a = 10;

ref int b = ref a; 引用传递

本地函数:函数内部的函数



评论(0)

查看评论列表

暂无评论


发表评论

表情 颜文字
插入代码

个人信息

avatar

24
文章
1
评论
1
用户

近期文章