有什么不明白的地方,扫描右方二维码加我微信交流。
       

在看以下知识前,请先学习ManualResetEvent的使用。

多线程任务队列有什么用?

例如在Unity中,像一些耗时操作,如文件IO,网络请求,复杂计算等等,如果在主线程做这些任务,可能会阻塞主线程,这样就会导致游戏卡顿,掉帧。这个时候就要在非主线程来处理这些任务。

实现原理:

  • 创建一个Thread跑一个Looper,就是一个死循环
  • 使用ManualResetEvent类来控制Looper线程的阻塞和非阻塞
  • 定义Hander类来向Looper中添加任务
  • 当Looper中有任务时,ManualResetEvent调用Set方法不阻塞线程,Looper处理任务;当Looper中的所有任务处理完成后,ManualResetEvent调用Reset方法和WaitOne方法阻塞线程

具体的代码实现如下:

using System;
using System.Collections.Generic;
using System.Threading;
using UnityEngine;

public class LoopThread
{
    public int ThreadId;
    
    private bool _isExiting;
    public Handler Handler;
    private Loop _loop;
    private Thread _thread;
    
    public void Start()
    {
        _isExiting = false;
        _loop = new Loop();
        Handler = new Handler(_loop);
        _thread = new Thread(Run);
        _thread.Start();
    }

    public void Exit()
    {
        _isExiting = true;
        _thread?.Abort();
    }
    
    void Run()
    {
        ThreadId = Thread.CurrentThread.ManagedThreadId;
        while (!_isExiting)
        {
            _loop.Prepare();
            while (true)
            {
                try
                {
                    if (!_loop.Execute())
                    {
                        break;
                    }
                }
                catch (Exception e)
                {
                    Debug.LogError("Loop failed, but continue");
                    Debug.LogException(e);
                }
            }
        }
    }
}

public class Loop
{
    private readonly List<Action> _actionQueue;
    private readonly ManualResetEvent _manualResetEvent;

    public Loop()
    {
        _manualResetEvent = new ManualResetEvent(false);
        _actionQueue = new List<Action>();
    }

    public void AddAction(Action action)
    {
        lock (_actionQueue)
        {
            _actionQueue.Add(action);
        }
        _manualResetEvent.Set();
    }

    public void Prepare()
    {
        _manualResetEvent.WaitOne();
        _manualResetEvent.Reset();
    }

    public bool Execute()
    {
        Action callback;
        lock (_actionQueue)
        {
            if (_actionQueue.Count == 0)
            {
                return false;
            }
            callback = _actionQueue[0];
            _actionQueue.RemoveAt(0);
        }

        try
        {
            callback();
        }
        catch (Exception e)
        {
            Debug.LogError("Execute callback failed");
            Debug.LogException(e);
        }

        return true;
    }
}

public class Handler
{
    private readonly Loop _loop;

    public Handler(Loop loop)
    {
        _loop = loop;
    }

    public void Post(Action act)
    {
        _loop.AddAction(act);
    }
}

以上为一个基类,可以实现其他类继承LoopThread。

例如我们要写一个文件IO的任务处理序列,则可以创建类FileIOLoopThread,继承LoopThread。然后简单封装一下Post接口,有更多需求的话自行封装,这里不再过多赘述。

 

发表评论

您的电子邮箱地址不会被公开。 必填项已用*标注