http://blog.sina.com.cn/s/blog_471132920101hh5d.html
https://www.jianshu.com/p/95784290a384
https://www.cnblogs.com/HangZhe/p/7273227.html
https://kb.cnblogs.com/page/88513/
https://www.cnblogs.com/OceanEyes/p/coroutine_vs_threading.html
https://github.com/rhedgeco/unity_multithreading_handler 代码
举例1:使用多线程
using System.Collections;
using System.Collections.Generic;
using System.Threading;
using UnityEngine;
public class UseThread : MonoBehaviour
{
private int count = 0;
void Start()
{
Thread t1 = new Thread(SayHello);
Thread t2 = new Thread(SayNihao);
t1.Name = "t1";
t2.Name = "t2";
t1.Start("xiaoming");
t2.Start(123);
}
private void SayHello(object param)
{
for (int i = 0; i < 300; ++i)
{
Debug.LogError("hello " + param.ToString() + " " + Thread.CurrentThread.Name + " " + Thread.CurrentThread.ManagedThreadId);
Thread.Sleep(1000);
}
}
private void SayNihao(object param)
{
for (int i = 0; i < 300; ++i)
{
Debug.LogError("你好 " + param.ToString() + " " + Thread.CurrentThread.Name + " " + Thread.CurrentThread.ManagedThreadId);
Thread.Sleep(1000);
}
}
void Update()
{
count++;
if (count % 1000 == 0)
{
Debug.LogError("mainThread " + Thread.CurrentThread.Name + " " + Thread.CurrentThread.ManagedThreadId);
count = 0;
}
}
}
上面在start方法中,创建了两个子线程。
线程1:执行SayHello方法
线程2:执行SayNihao方法
主线程:执行Update方法
举例2:两个子线程同时访问queue
using System.Collections;
using System.Collections.Generic;
using System.Threading;
using UnityEngine;
public class UseQueueInMultiThread : MonoBehaviour
{
private int count = 0;
private Queue<string> m_queue = new Queue<string>();
private static readonly object m_lock = new object();
void Start()
{
Thread t1 = new Thread(SayHello);
Thread t2 = new Thread(SayNihao);
t1.Name = "t1";
t2.Name = "t2";
t1.Start("xiaoming");
t2.Start(123);
}
private void SayHello(object param)
{
for (int i = 0; i < 300; ++i)
{
lock(m_lock) //如果未加锁,则导致最后输出的m_queue.count不等于总共的600
{
m_queue.Enqueue(i + " " + param.ToString());
}
Thread.Sleep(10);
}
}
private void SayNihao(object param)
{
for (int i = 0; i < 300; ++i)
{
lock (m_lock) //如果未加锁,则导致最后输出的m_queue.count不等于总共的600
{
m_queue.Enqueue(i + " " + param.ToString());
}
Thread.Sleep(10);
}
}
void Update()
{
count++;
if (count % 200 == 0)
{
Debug.LogError("queue count=" + m_queue.Count);
count = 0;
}
}
}
上面如果将lock(m_lock)去除掉,则会看到在update中最后的输出,有可能不等于600。
举例3:主线程的卡死问题
这个问题,还是很难,或者很容易解决的。
lock的最后编译成的是:
try
{
Monitor.Enter
……
Monitor.Exit
}
finally
{
……
}
参考:https://blog.csdn.net/wodownload2/article/details/119170818
但是如果在Enter和Exit直接报错或者出现exception的话,会直接卡死主线程。示例代码:
using System;
using System.Collections;
using System.Collections.Generic;
using System.Threading;
using UnityEngine;
public class UseQueueInMultiThread : MonoBehaviour
{
private int count = 0;
private Queue<string> m_queue = new Queue<string>();
private static readonly object m_lock = new object();
private Thread t1;
private Thread t2;
private List<int> m_list = new List<int>();
void Start()
{
t1 = new Thread(SayHello);
//t2 = new Thread(SayNihao);
t1.Name = "t1";
//t2.Name = "t2";
t1.Start("xiaoming");
//t2.Start(123);
}
private void SayHello(object param)
{
for (int i = 0; i < 300; ++i)
{
//lock (m_lock) //如果未加锁,则导致最后输出的m_queue.count不等于总共的600
//try
{
Monitor.Enter(m_lock);
m_queue.Enqueue(i + " " + param.ToString());
Debug.LogError("SayHello " + i);
if (i == 100)
{
//throw new System.Exception("xxxxxxxxxxx");
Debug.LogError("dd = " + m_list[1]); //,如果使用Monitor,且不try catch,当数组越界会卡死主线程
}
Debug.LogError("yyyyyyyyyyyy");
}
//catch (Exception ex)
//{
// Debug.LogError("ddaaaaaaaa " + ex.ToString());
//}
Monitor.Exit(m_lock);
Thread.Sleep(10);
}
}
private void SayNihao(object param)
{
for (int i = 0; i < 300; ++i)
{
//lock (m_lock) //如果未加锁,则导致最后输出的m_queue.count不等于总共的600
Monitor.Enter(m_lock);
{
m_queue.Enqueue(i + " " + param.ToString());
Debug.LogError("SayNihao " + i);
}
Monitor.Exit(m_lock);
Thread.Sleep(10);
}
}
void Update()
{
count++;
if (count % 10 == 0)
{
Debug.LogError("queue count=" + m_queue.Count + " " + t1.ThreadState + " " + t2.ThreadState);
count = 0;
}
Monitor.Enter(m_lock); //这里在等待m_lock锁
Debug.LogError("xxxxxxxxxxaaaaaaaaaaaa");
Monitor.Exit(m_lock);
}
}
上面的在t1线程中,执行SayHello的时候,数组越界。同时没有在Monitor.Enter和Monitor.Exit之间进行try catch,所以当发生崩溃的时候无法执行Monitor.Exit,此时子线程t1,未释放m_lock。
而主线程在Update中,等待m_lock,所以此时主线程卡死。
解决的方法有两个:
1、使用try catch,保证在Monitor.Enter和Monitor.Exit执行的代码,当发生崩溃、报错的时候,能执行Monitor.Exit,保证锁的释放。
2、或者采样lock(m_lock)的方式,不使用Monitor.Enter和Monitor.Exit方式,也是可以避免崩溃的。
卡死主线程的例子:
using FMODUnity;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Threading;
using UnityEngine;
public class MultiThread : MonoBehaviour
{
private static readonly object m_lock = new object();
private Thread t1;
private Thread t2;
private int a = 1;
private GameObject m_go;
private float m_preTime;
void Start()
{
Debug.LogError("Start");
t1 = new Thread(SayHello);
t1.Name = "t1";
t1.Start("xiaoming");
t2 = new Thread(Log);
t2.Name = "t2";
t2.Start();
}
private void Log()
{
while (true)
{
Debug.LogError("sub thead2xxxxxxxxxxx");
Thread.Sleep(1000);
}
}
private void SayHello(object param)
{
while(true)
{
Monitor.Enter(m_lock);
Debug.LogError("sub thead");
a++;
if (a == 15)
{
Debug.LogError("sub thread " + a);
StudioEventEmitter studioEventEmitter = m_go.GetComponent<StudioEventEmitter>();
}
Monitor.Exit(m_lock);
Thread.Sleep(1000);
}
}
void Update()
{
Monitor.Enter(m_lock);
Debug.LogError("main thead");
Monitor.Exit(m_lock);
Thread.Sleep(1000);
}
private void OnDestroy()
{
t1.Abort();
t2.Abort();
}
public void OnStop()
{
Debug.LogError("stopxaaa");
}
}
主线程卡死了,子线程t2还可以继续执行。