ue3dUnity多线程独占控制并行处理的方法总结

Unity多线程独占控制并行处理的方法总结

分类:
ue3d - Unity多线程独占控制并行处理的方法总结

本文介绍了C#中多线程独占控制并行处理的方法总结,虽然它并不特别依赖于Unity,但已经检查了Unity的操作情况。

独占控制的必要性

在进行多线程并行处理时,需要注意共享变量的处理,例如创建100个任务,将一个变量递增100次。

如下所示:

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

public class Example : MonoBehaviour
{
    public async void Start()
    {
        var result = 0;
        
        // 创建将结果递增 100 倍的任务的方法
        Task CreateTask()
        {
            return Task.Run(() =>
            {
                for (var m = 0; m < 100; m++) {
                    result++; // 不宜同时加工的部分(关键部分)
                }
            });
        }
        
        // 创建上面100个任务并等待完成
        var tasks = new List<Task>();
        for (var i = 0; i < 100; i++)
        {
            tasks.Add(CreateTask());
        }
        await Task.WhenAll(tasks);
        
        // 输出结果为 7555、9558、8613、9015,每次执行都会有所不同(预期结果为 10000)
        Debug.Log(result);
    }
}

此时预期结果为100*100=10000,但实际结果随着7555、9558、8613、9015每次执行而变化,这是因为当一个线程正在执行计算时,其他线程的计算也在执行。

像这个增量部分这样不应该由多个线程同时处理的部分称为临界区,下面总结了临界区独占控制的方法。

基本独占处理:lockObject

第一个是lockObject的独占控制,所要做的就是创建一个对象类型实例并用lock语句将临界区括起来。

如下面的注释所示:

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

public class Example : MonoBehaviour
{
    public async void Start()
    {
        var result = 0;
        
        // 实例化对象
        var lockObject = new object();
        
        Task CreateTask()
        {
            return Task.Run(() =>
            {
                for (var m = 0; m < 100; m++) {
                    // 用 lock(lockObject){} 将临界区括起来
                    lock (lockObject)
                    {
                        result++;
                    }
                }
            });
        }
        
        var tasks = new List<Task>();
        for (var i = 0; i < 100; i++)
        {
            tasks.Add(CreateTask());
        }
        await Task.WhenAll(tasks);
        
        Debug.Log(result);
    }
}

成功运行此命令会产生10000个结果。

简单的独家处理:interlock

Interlocked也可以用于简单的运算,如递增、递减、加法等。通过使用定义为Interlocked的类,可以很容易地执行独占控制。

如下所示:

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

public class Example : MonoBehaviour
{
    public async void Start()
    {
        var result = 0;
        
        Task CreateTask()
        {
            return Task.Run(() =>
            {
                for (var m = 0; m < 100; m++) {
                    // 当使用 Interlocked 方法进行处理时,该部分受到独占控制
                    Interlocked.Increment(ref result);
                }
            });
        }
        
        var tasks = new List<Task>();
        for (var i = 0; i < 100; i++)
        {
            tasks.Add(CreateTask());
        }
        await Task.WhenAll(tasks);
        
        Debug.Log(result);
    }
}

设置并行数上限:SemaphoreSlim

信号量是可以并行执行的数字,例如,通过如下使用SemaphoreSlim类,可以将可并行化的最大任务数设置为3。

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

public class Example : MonoBehaviour
{
    [SerializeField] private int _threadCount;
    [SerializeField] private int _completeThreadCount;

    public async void Start()
    {
        // 创建一个上限为 3 的信号量
        var semaphore = new SemaphoreSlim(3);
        
        Task CreateTask()
        {
            return Task.Run(() =>
            {
                try
                {
                    // 如果信号量数量达到上限则等待
                    semaphore.Wait();
                    Interlocked.Increment(ref _threadCount);
                    Thread.Sleep(300);
                    Interlocked.Decrement(ref _threadCount);
                    Interlocked.Increment(ref _completeThreadCount);
                }
                finally
                {
                    // 释放信号量
                    semaphore.Release();
                }
            });
        }
        
        var tasks = new List<Task>();
        for (var i = 0; i < 100; i++)
        {
            tasks.Add(CreateTask());
        }
        await Task.WhenAll(tasks);
        
        Debug.Log("Complete");
    }
}

如果播放这个并查看检查器,可以看到任务始终以上限3运行。

ue3d - Unity多线程独占控制并行处理的方法总结

Semaphore和SemaphoreSlim

除了SemaphreSlim之外,还有一个Semaphore类,它的使用方式与SemaphoreSlim相同。

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

public class Example : MonoBehaviour
{
    [SerializeField] private int _threadCount;
    [SerializeField] private int _completeThreadCount;

    public async void Start()
    {
        // 创建一个上限为 3 的信号量
        var semaphore = new Semaphore(3, 3);
        
        Task CreateTask()
        {
            return Task.Run(() =>
            {
                try
                {
                    // 如果信号量数量达到上限则等待
                    semaphore.WaitOne();
                    Interlocked.Increment(ref _threadCount);
                    Thread.Sleep(300);
                    Interlocked.Decrement(ref _threadCount);
                    Interlocked.Increment(ref _completeThreadCount);
                }
                finally
                {
                    // 释放信号量
                    semaphore.Release();
                }
            });
        }
        
        var tasks = new List<Task>();
        for (var i = 0; i < 100; i++)
        {
            tasks.Add(CreateTask());
        }
        await Task.WhenAll(tasks);
        
        Debug.Log("Complete");
    }
}

然而,这似乎在多进程应用程序中使用,因为它可以被其他进程命名和引用,可以从这里了解Semaphore and SemaphoreSlim。

事件等待句柄

没有必要像这次那样使用它来进行独占控制,但是也有一种方法可以使用EventWaitHandle。

以上是关于Unity多线程独占控制并行处理的方法的全部内容,如果你有任何反馈,请随时在本页面下方留言。

相关信息

  • 类型:知识
  • 字数:690
  • 字符:6719
  • 适用软件:Unity2019.4.4
  • 说明:无
  • 编号:165516

热门内容

提示:3D天堂作为服务提供者,尊重网络版权及知识产权,对某些行为的发生不具备充分的监控能力,若无意间侵犯到您的权利,请 联系我们,我们会在收到信息后尽快给予处理。

本站文章版权归本站自创作者所有,未经允许不得转载!