Unity中多线程设计与实现

    • 前言
    • 什么多线程
    • 在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 _actions = new List();
    private List _currentActions = new List();
    private List _delayed = new List();
    private List _currentDelayed = new List();

#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();

    }

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

你可能感兴趣的:(Programme,Unity)