Unity中多线程设计与实现

来源:互联网 发布:单机游戏下载软件排行 编辑:程序博客网 时间:2024/05/16 12:19

    • 前言
    • 什么多线程
    • 在Unity中使用多线程能干什么
    • 举栗子的时间到了
    • 写在最后

前言

因为项目后期,好久没有更博了。最近项目推上线了,突然闲下来了。想写的东西很多。就先从多线程开始吧。
个人对线程的理解还不够透彻,希望如果有更好的看法可以在评论区提出,感谢。

什么多线程

在一个程序中,一些独立运行的程序片段叫作“线程”(Thread),利用它编程的概念就叫作“多线程处理”(Multithreading)。

在Unity中使用多线程能干什么?

  1. 使用Unity开发的应用,只能在主线程中访问Unity组件,这一点限制了部分的优化。不过小伙伴可以开一些辅助线程。

  2. 允许开线程来辅助的功能业务

    • 大量的数据计算 (eg. A*寻路算法之类的)
    • IO操作
    • 解压缩资源
    • 不涉及Unity组件的资源加载(Ps.关于涉及Unity组件的加载可以通过逻辑剥离来实现,这边只抛出一个idea供大家发散。)

举栗子的时间到了

  • 首先就是继承MonoBehaviour啦。
/*----------------------------------------------------------------            // Copyright © 2016 Jhon            //             // FileName: Loom.cs            // Describle:            // Created By:  Jhon            // Date&Time:  2016年10月14日 17:31:26            // Modify History:             ////--------------------------------------------------------------*/using UnityEngine;using System.Collections;using System.Collections.Generic;using System;using System.Threading;public class Loom : MonoBehaviour{}
  • 定义一个线程处理的 Item 结构,以及变量的定义。
#region StructMember    public struct DelayedQueueItem    {        public float time;        public Action action;    }#endregion#region Member    public static int mMaxThreads = 8;    private static Loom _current;    public static Loom Current    {        get        {            Initialize();            return _current;        }    }    private static int mNumThreads;    private static bool initialized;    private int _count;    private List<Action> _actions = new List<Action>();    private List<Action> _currentActions = new List<Action>();    private List<DelayedQueueItem> _delayed = new List<DelayedQueueItem>();    private List<DelayedQueueItem> _currentDelayed = new List<DelayedQueueItem>();#endregion
  • 借助MonoBehaviour的生命周期来处理业务逻辑
#region MonoLife    void Awake()    {        _current = this;        initialized = true;    }    void OnEnable()    {        if (null == _current)        {            _current = this;        }    }    void OnDisable()    {        if (this == _current)        {            _current = null;        }    }    void Update()    {        lock (_actions)        {            _currentActions.Clear();            _currentActions.AddRange(_actions);            _actions.Clear();        }        for (int i = 0;i < _currentActions.Count;i++)        {            _currentActions[i]();        }        lock (_delayed)        {            _currentDelayed.Clear();            for(int i = _delayed.Count - 1;i > 0;i--)            {                if(_delayed[i].time > Time.time)                {                    continue;                }                _currentDelayed.Add(_delayed[i]);                _delayed.RemoveAt(i);            }        }        for(int i = 0;i < _currentDelayed.Count;i++)        {            _currentDelayed[i].action();        }    }#endregion
  • 业务逻辑
#region BusinessLogic    private static void Initialize()    {        if (initialized == true)return;        if (Application.isPlaying == false)return;        initialized = true;        var g = new GameObject("Loom");        _current = g.AddComponent<Loom>();    }    private static void RunAction(object action)    {        try        {            ((Action)action)();        }        catch(Exception e)        {            Log.Error(e.Message);        }        finally        {            Interlocked.Decrement(ref mNumThreads);        }    }#endregion
  • 单例模式对外提供的API
#region PublicTools    public static void QueueOnMainThread(Action action, float time = 0.0f)    {        if (time != 0)        {            lock (Current._delayed)            {                Current._delayed.Add(new DelayedQueueItem { time = Time.time + time, action = action });            }        }        else        {            lock (Current._actions)            {                Current._actions.Add(action);            }        }    }    public static Thread RunAsync(Action varAction)    {        Initialize();        while (mNumThreads >= mMaxThreads)        {            Thread.Sleep(1);        }        Interlocked.Increment(ref mNumThreads);        ThreadPool.QueueUserWorkItem(RunAction, varAction);        return null;    }#endregion

写在最后

Update函数中可以使用foreach来进行遍历,在我的 另一篇博客 中有提到使用foreach会有16BGC。鉴于是在Update中进行的操作,推荐使用for来进行遍历。下面贴出foreach版本。

    private void OldVersionUpdate()    {        lock (_actions)        {            _currentActions.Clear();            _currentActions.AddRange(_actions);            _actions.Clear();        }        foreach (var a in _currentActions)        {            a();        }        lock (_delayed)        {            _currentDelayed.Clear();            _currentDelayed.AddRange(_delayed.Where(d => d.time <= Time.time));            foreach (var item in _currentDelayed)                _delayed.Remove(item);        }        foreach (var delayed in _currentDelayed)        {            delayed.action();        }    }
1 0