Unity multi-threaded data acquisition (using Loom + Messenger)

A new project, after working for more than two months, I found that forgetting to write multi-threaded to obtain data caused a lag, and then tried to change it, but did not want to disrupt the structure of the active and passive parts, and used Loom to solve the sub-thread after the completion of the main thread. broadcast.
Messenger itself will be used in a sub-thread get_isActiveAndEnabled can only be called from the main thread.error, but if you take the initiative to get the data into the main thread, but it will not still stuck in a static problem with the above, because I Xueyibujing can not solve the problem of static not static, and finally found implemented Loom Use Messenger's Broadcast in the child thread.

Loom code, hang on the SceneManager, if there are several Scenes, hang on the GameObject of the DoNotDestroy class.

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

Visually, this can hold up to 8 threads. I only use one at present, so other pits may be added in the future.

For the thread part, you need to use Loom to create the thread part first.

void Start()
{
    
    
	//开启一个线程连接,必须的,否则主线程卡死(正常操作)
	//connectThread = new Thread(new ThreadStart(ThreadToParse));
	//connectThread.Start();
	
	
	// 用Loom的方法调用一个线程
	Loom.RunAsync(
	    () =>
	    {
    
    
	        Thread thread = new Thread(ThreadToParse);
	        thread.Start();
	    }
	    );
}

I didn't find this example in the thread closing part, and it does report an error when it stops running. Let's look at filling this hole in the future.

void OnDestroy()
{
    
    
    //关闭线程(正常操作)
    if (connectThread != null)
    {
    
    
        //connectThread.Interrupt();
        connectThread.Abort();
    }
}

Connecting the main thread part:
it can also carry any required data in the broadcast normally .
,nullIt refers to the delay time, which can be removed if not needed.

Loom.QueueOnMainThread(
	() => 
	{
    
     
		Messenger.Broadcast("WhatEverYouWantToBroadcast"); 
	}, 
	null);

this one.
The other unfilled pits are filled in at random, maybe one day I step on this pit again and I will fill it. .

Related reading: For the
broadcasting part, please refer to this tutorial: https://www.xuanyusong.com/archives/2165

Guess you like

Origin blog.csdn.net/MikeW138/article/details/107025591