一个新建项目,做了两个多月才发现忘记写多线程获取数据导致卡顿,然后尝试去改动,但是不想把主动被动的部分的结构打乱,使用了Loom解决子线程完成后向主线程广播。
Messenger本身用在子线程会出现 get_isActiveAndEnabled can only be called from the main thread.
的报错,但是如果改成主线程主动获取数据,却会卡在静态不静态的问题上面,由于我学艺不精无法解决static不static的问题,最后找到用Loom实现在子线程使用Messenger的Broadcast。
Loom代码,挂在SceneManager上面,如果有几个Scene的话则挂在DoNotDestroy类的GameObject上面。
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
using System;
using System.Threading;
using System.Linq;
/// <summary>
/// Loom 用以在主线程和子线程间传递。
/// https://blog.csdn.net/luoyikun/article/details/78650904
/// http://www.voidcn.com/article/p-slikogln-da.html
/// </summary>
public class Loom : MonoBehaviour
{
public static int maxThreads = 8;
static int numThreads;
private static Loom _current;
private int _count;
public static Loom Current
{
get
{
Initialize();
return _current;
}
}
void Awake()
{
_current = this;
initialized = true;
}
static bool initialized;
static void Initialize()
{
if (!initialized)
{
if (!Application.isPlaying)
return;
initialized = true;
var g = new GameObject("Loom");
_current = g.AddComponent<Loom>();
}
}
private List<Action> _actions = new List<Action>();
public struct DelayedQueueItem
{
public float time;
public Action action;
}
private List<DelayedQueueItem> _delayed = new List<DelayedQueueItem>();
List<DelayedQueueItem> _currentDelayed = new List<DelayedQueueItem>();
public static void QueueOnMainThread(Action action)
{
QueueOnMainThread(action, 0f);
}
public static void QueueOnMainThread(Action action, float time)
{
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 a)
{
Initialize();
while (numThreads >= maxThreads)
{
Thread.Sleep(1);
}
Interlocked.Increment(ref numThreads);
ThreadPool.QueueUserWorkItem(RunAction, a);
return null;
}
private static void RunAction(object action)
{
try
{
((Action)action)();
}
catch
{
}
finally
{
Interlocked.Decrement(ref numThreads);
}
}
void OnDisable()
{
if (_current == this)
{
_current = null;
}
}
// Use this for initialization
void Start()
{
}
List<Action> _currentActions = new List<Action>();
// Update is called once per frame
void Update()
{
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();
}
}
}
目测这个可以最多容纳8个线程,目前我只用到1个,所以以后可能会补充其他坑。
线程部分,首先创建线程部分就需要用Loom的。
void Start()
{
//开启一个线程连接,必须的,否则主线程卡死(正常操作)
//connectThread = new Thread(new ThreadStart(ThreadToParse));
//connectThread.Start();
// 用Loom的方法调用一个线程
Loom.RunAsync(
() =>
{
Thread thread = new Thread(ThreadToParse);
thread.Start();
}
);
}
关闭线程部分我没有找到这个例子,目前停止运行的时候的确会报错,这坑以后看着填吧。
void OnDestroy()
{
//关闭线程(正常操作)
if (connectThread != null)
{
//connectThread.Interrupt();
connectThread.Abort();
}
}
连接主线程部分:
也可以正常地在广播中带有任何需要的数据。
,null
是指延迟时间,不需要的话也可以去掉。
Loom.QueueOnMainThread(
() =>
{
Messenger.Broadcast("WhatEverYouWantToBroadcast");
},
null);
就这。
其他没有填的坑随缘填,说不定哪天我又踩回来这个坑我就会填。。
相关阅读:
广播部分可以参考这个教程:https://www.xuanyusong.com/archives/2165