索引
1、用委托(Delegate)的BeginInvoke和EndInvoke方法操作线程
2、使用IAsyncResult.IsCompleted属性来判断异步调用是否完成
相关文档
C# CancellationTokenSource 终止线程
(1)不需要传递参数,也不需要返回参数
ThreadStart是一个委托,这个委托的定义为void ThreadStart(),没有参数与返回值。
using UnityEngine;
using System.Collections;
using System.Threading;
using System;
public class TestThreadStart : MonoBehaviour {
public string data = "";
public void Test ()
{
StartCoroutine(ShowData());
DateTime time = DateTime.Now;//得到当前时间
Debug.Log("Begin " + time.Minute + ":" + time.Second);
for (int i = 0; i < 1; i++)
{
ThreadStart threadStart = new ThreadStart(Calculate);
Thread thread = new Thread(threadStart);
thread.Start();
}
// Thread.Sleep(2000); // 此时Unity会卡死
time = DateTime.Now;
Debug.Log("End " + time.Minute + ":" + time.Second);
}
public void Calculate()
{
DateTime time = DateTime.Now;//得到当前时间
Debug.Log("Calculate Begin" + time.Minute + ":" + time.Millisecond);
for(int i = 0; i < 10; i ++)
{
Thread.Sleep(1000);//随机休眠一段时间
time = DateTime.Now;
Debug.Log("Calculate i=" + i +" " + time.Minute + ":" + time.Second );
data = "i--" + i;
}
Debug.Log("Calculate End" + time.Minute + ":" + time.Millisecond);
}
IEnumerator ShowData()
{
while (true)
{
Debug.Log("data=" + data);
yield return new WaitForSeconds(1F);
}
}
}
[out]
data= Begin 54:20 Calculate Begin54:843 End 54:20 data= Calculate i=0 54:21 Calculate i=1 54:22 data=i--1 data=i--1 Calculate i=2 54:23 Calculate End54:857 data=i--2 data=i--2
(2)需要传递单个参数
ParameterThreadStart委托定义为void ParameterizedThreadStart(object state),有一个参数但是没有返回值。
using UnityEngine;
using System.Collections;
using System.Threading;
using System;
public class TestParameterThreadStart : MonoBehaviour {
public void Test ()
{
DateTime time = DateTime.Now;//得到当前时间
Debug.Log("Begin " + time.Minute + ":" + time.Second);
ParameterizedThreadStart tStart = new ParameterizedThreadStart(Calculate);
Thread thread = new Thread(tStart);
thread.Start("Hello");//传递参数
time = DateTime.Now;
Debug.Log("End " + time.Minute + ":" + time.Second);
}
public void Calculate(object arg)
{
Debug.Log("Calculate arg=" + arg);
for(int i = 0; i < 3; i ++)
{
Thread.Sleep(1000);//随机休眠一段时间
Debug.Log("Calculate " + i + " " + DateTime.Now);
}
}
}
[out]
Begin 2:0 Calculate arg=Hello End 2:0 Calculate 0 01/05/2017 17:02:01 Calculate 1 01/05/2017 17:02:02 Calculate 2 01/05/2017 17:02:03
(3)使用专门的线程类(常用)
使用线程类可以有多个参数与多个返回值,十分灵活!
using UnityEngine;
using System.Collections;
using System.Threading;
public class TestThreadClass : MonoBehaviour
{
public void Test()
{
StartCoroutine(OnTest());
}
IEnumerator OnTest()
{
Ctl ctl = new Ctl(2);
ThreadStart threadStart = new ThreadStart(ctl.Calculate);
Thread thread = new Thread(threadStart);
Debug.Log("[Main] [before] thread.ThreadState=" + thread.ThreadState);
thread.Start();
//等待线程结束
while (thread.ThreadState != ThreadState.Stopped)
{
Debug.Log("[Main] thread.ThreadState=" + thread.ThreadState);
yield return new WaitForSeconds(0.5f);
}
Debug.Log("[Main] [end] thread.ThreadState=" + thread.ThreadState);
Debug.Log("[Main] ctl.Result=" + ctl.Result);
}
public class Ctl
{
public int Parame { set; get; }//参数
public int Result { set; get; }//返回值
//构造函数
public Ctl(int parame)
{
this.Parame = parame;
}
//线程执行方法
public void Calculate()
{
for(int i = 0; i < 3; i ++)
{
Thread.Sleep(2000);//随机休眠一段时间
this.Result += this.Parame * 10;
Debug.Log("Calculate i=" +i+ ", Result=" + Result);
}
Debug.Log("Calculate Parame=" + Parame);
Debug.Log("Calculate Result=" + Result);
}
}
}
[out]
[Main] [before] thread.ThreadState=Unstarted [Main] thread.ThreadState=Running [Main] thread.ThreadState=WaitSleepJoin [Main] thread.ThreadState=WaitSleepJoin [Main] thread.ThreadState=WaitSleepJoin Calculate i=0, Result=20 [Main] thread.ThreadState=WaitSleepJoin [Main] thread.ThreadState=WaitSleepJoin [Main] thread.ThreadState=WaitSleepJoin [Main] thread.ThreadState=WaitSleepJoin Calculate i=1, Result=40 [Main] thread.ThreadState=WaitSleepJoin [Main] thread.ThreadState=WaitSleepJoin [Main] thread.ThreadState=WaitSleepJoin [Main] thread.ThreadState=WaitSleepJoin Calculate i=2, Result=60 Calculate Parame=2 Calculate Result=60 [Main] [end] thread.ThreadState=Stopped [Main] ctl.Result=60
(4)使用匿名方法(常用)
使用匿名方法启动线程可以有多个参数和返回值,而且使用非常方便!
using UnityEngine;
using System.Collections;
using System.Threading;
using System;
public class TestThreadDelegate : MonoBehaviour
{
public void Test()
{
StartCoroutine(OnTest());
}
IEnumerator OnTest()
{
int Parame = 2;//当做参数
int Result = 0;//当做返回值
//匿名方法
ThreadStart threadStart = new ThreadStart(delegate()
{
for(int i = 0; i < 3; i ++)
{
Thread.Sleep(2000);//随机休眠一段时间
Result += Parame * 10;
Debug.Log(time +" [Thread] i=" +i+ ", Result=" + Result);
}
Debug.Log(time +" [Thread] Parame=" + Parame);
Debug.Log(time +" [Thread] Result=" + Result);
});
Thread thread = new Thread(threadStart);
Debug.Log(time +" [Main] [before] thread.ThreadState=" + thread.ThreadState);
thread.Start();
//等待线程结束
while (thread.ThreadState != ThreadState.Stopped)
{
Debug.Log(time +" [Main] thread.ThreadState=" + thread.ThreadState + " Result=" + Result);
yield return new WaitForSeconds(1f);
}
Debug.Log(time +" [Main] [end] thread.ThreadState=" + thread.ThreadState);
Debug.Log(time +" [Main] ctl.Result=" + Result);
}
public string time
{
get
{
return DateTime.Now.ToString("mm:ss:ffff");
}
}
}
[out]
30:41:4919 [Main] [before] thread.ThreadState=Unstarted 30:41:4993 [Main] thread.ThreadState=Running Result=0 30:42:4906 [Main] thread.ThreadState=WaitSleepJoin Result=0 30:43:4906 [Main] thread.ThreadState=WaitSleepJoin Result=0 30:43:5043 [Thread] i=0, Result=20 30:44:4912 [Main] thread.ThreadState=WaitSleepJoin Result=20 30:45:5068 [Main] thread.ThreadState=WaitSleepJoin Result=20 30:45:5072 [Thread] i=1, Result=40 30:46:5076 [Main] thread.ThreadState=WaitSleepJoin Result=40 30:47:5100 [Main] thread.ThreadState=WaitSleepJoin Result=40 30:47:5118 [Thread] i=2, Result=60 30:47:5121 [Thread] Parame=2 30:47:5124 [Thread] Result=60 30:48:5102 [Main] [end] thread.ThreadState=Stopped 30:48:5106 [Main] ctl.Result=60
(5)使用委托开启多线程(多线程深入)
1、用委托(Delegate)的BeginInvoke和EndInvoke方法操作线程
BeginInvoke方法可以使用线程异步地执行委托所指向的方法。然后通过EndInvoke方法获得方法的返回值(EndInvoke方法的返回值就是被调用方法的返回值),或是确定方法已经被成功调用。
using UnityEngine;
using System.Collections;
using System.Threading;
using System;
public class TestThreadInvoke : MonoBehaviour {
public void Test()
{
NewTaskDelegate task = newTask;
Log("Main", "BeginInvoke Before");
IAsyncResult asyncResult = task.BeginInvoke(2000, null, null);
Log("Main", "EndInvoke Before");
//EndInvoke方法将被阻塞2秒
int result = task.EndInvoke(asyncResult);
Log("Main", "result=" + result);
}
private delegate int NewTaskDelegate(int ms);
private int newTask(int ms)
{
Log("Task", "任务开始");
Thread.Sleep(ms);
System.Random random = new System.Random();
int n = random.Next(10000);
Log("Task", "任务完成");
return n;
}
private void Log(string threadName, string msg)
{
Debug.LogFormat("{0} [{1}] {2}", time, threadName, msg);
}
public string time
{
get
{
return DateTime.Now.ToString("mm:ss:ffff");
}
}
}
[out]
33:39:8422 [Main] BeginInvoke Before 33:39:8535 [Task] 任务开始 33:39:8534 [Main] EndInvoke Before 33:41:8590 [Task] 任务完成 33:41:8595 [Main] result=8484
2、使用IAsyncResult.IsCompleted属性来判断异步调用是否完成
using UnityEngine;
using System.Collections;
using System.Threading;
using System;
public class TestThreadInvokeIsCompleted : MonoBehaviour {
public void Test()
{
NewTaskDelegate task = newTask;
Log("Main", "BeginInvoke Before");
IAsyncResult asyncResult = task.BeginInvoke(2000, null, null);
//等待异步执行完成
while(!asyncResult.IsCompleted)
{
Log("Main", "asyncResult.IsCompleted=" + asyncResult.IsCompleted);
Thread.Sleep(500);
}
Log("Main", "EndInvoke Before");
// 由于异步调用已经完成,因此, EndInvoke会立刻返回结果
int result = task.EndInvoke(asyncResult);
Log("Main", "result=" + result);
}
private delegate int NewTaskDelegate(int ms);
private int newTask(int ms)
{
Log("Task", "任务开始");
Thread.Sleep(ms);
System.Random random = new System.Random();
int n = random.Next(10000);
Log("Task", "任务完成");
return n;
}
private void Log(string threadName, string msg)
{
Debug.LogFormat("{0} [{1}] {2}", time, threadName, msg);
}
public string time
{
get
{
return DateTime.Now.ToString("mm:ss:ffff");
}
}
}
[out]
44:42:9893 [Main] BeginInvoke Before 44:42:9946 [Task] 任务开始 44:42:9943 [Main] asyncResult.IsCompleted=False 44:43:4965 [Main] asyncResult.IsCompleted=False 44:43:9977 [Main] asyncResult.IsCompleted=False 44:44:4996 [Main] asyncResult.IsCompleted=False 44:45:0006 [Task] 任务完成 44:45:0010 [Main] asyncResult.IsCompleted=False 44:45:5025 [Main] EndInvoke Before 44:45:5038 [Main] result=2526
3、使用WaitOne方法等待异步方法执行完成
WaitOne的第一个参数表示要等待的毫秒数,在指定时间之内,WaitOne方法将一直等待,直到异步调用完成,并发出通知,WaitOne方法才返回true。当等待指定时间之后,异步调用仍未完成,WaitOne方法返回false,如果指定时间为0,表示不等待,如果为-1,表示永远等待,直到异步调用完成。
using UnityEngine;
using System.Collections;
using System.Threading;
using System;
public class TestThreadInvokeWaitOne : MonoBehaviour {
public void Test()
{
NewTaskDelegate task = newTask;
Log("Main", "BeginInvoke Before");
IAsyncResult asyncResult = task.BeginInvoke(2000, null, null);
//等待异步执行完成
while(!asyncResult.AsyncWaitHandle.WaitOne(500))
{
Log("Main", "asyncResult.IsCompleted=" + asyncResult.IsCompleted);
}
Log("Main", "EndInvoke Before");
// 由于异步调用已经完成,因此, EndInvoke会立刻返回结果
int result = task.EndInvoke(asyncResult);
Log("Main", "result=" + result);
}
private delegate int NewTaskDelegate(int ms);
private int newTask(int ms)
{
Log("Task", "任务开始");
Thread.Sleep(ms);
System.Random random = new System.Random();
int n = random.Next(10000);
Log("Task", "任务完成");
return n;
}
private void Log(string threadName, string msg)
{
Debug.LogFormat("{0} [{1}] {2}", time, threadName, msg);
}
public string time
{
get
{
return DateTime.Now.ToString("mm:ss:ffff");
}
}
}
[out]
04:27:0838 [Main] BeginInvoke Before 04:27:0894 [Task] 任务开始 04:27:5896 [Main] asyncResult.IsCompleted=False 04:28:0907 [Main] asyncResult.IsCompleted=False 04:28:5914 [Main] asyncResult.IsCompleted=False 04:29:0930 [Main] asyncResult.IsCompleted=False 04:29:0939 [Task] 任务完成 04:29:0943 [Main] EndInvoke Before 04:29:0954 [Main] result=3246
[out] WaitOne(-1)
06:20:8036 [Main] BeginInvoke Before 06:20:8114 [Task] 任务开始 06:22:8166 [Task] 任务完成 06:22:8172 [Main] EndInvoke Before 06:22:8181 [Main] result=2112
[out] WaitOne(0)
08:12:2329 [Main] BeginInvoke Before 08:12:2388 [Task] 任务开始 // 这里相当于每帧调用一次 08:12:2398 [Main] asyncResult.IsCompleted=False 08:12:2406 [Main] asyncResult.IsCompleted=False . . . 08:14:2183 [Main] asyncResult.IsCompleted=False 08:14:2422 [Task] 任务完成 08:14:2193 [Main] asyncResult.IsCompleted=False 08:14:2550 [Main] EndInvoke Before 08:14:2563 [Main] result=2920
4、使用回调方式返回结果
要注意的是“my.BeginInvoke(3,300, MethodCompleted, my)”,BeginInvoke方法的参数传递方式:
前面一部分(3,300)是其委托本身的参数。
倒数第二个参数(MethodCompleted)是回调方法委托类型,他是回调方法的委托,此委托没有返回值,有一个IAsyncResult类型的参数,当method方法执行完后,系统会自动调用MethodCompleted方法。
最后一个参数(my)需要向MethodCompleted方法中传递一些值,一般可以传递被调用方法的委托,这个值可以使用IAsyncResult.AsyncState属性获得。
using UnityEngine;
using System.Collections;
using System.Threading;
using System;
public class TestThreadInvokeCallback : MonoBehaviour {
public void Test()
{
NewTaskDelegate task = newTask;
Log("Main", "BeginInvoke Before");
IAsyncResult asyncResult = task.BeginInvoke(2, 500, onCallBack, task);
// //等待异步执行完成
// while(!asyncResult.AsyncWaitHandle.WaitOne(500))
// {
// Log("Main", "asyncResult.IsCompleted=" + asyncResult.IsCompleted);
// }
//
// Log("Main", "EndInvoke Before");
// // 由于异步调用已经完成,因此, EndInvoke会立刻返回结果
// // 当有回调调用EndInvoke后 Main线程不能再调用EndInvoke方法
// int result = task.EndInvoke(asyncResult);
//
// Log("Main", "result=" + result);
}
//回调方法 此方法不是Main线程的
private void onCallBack(IAsyncResult asyncResult)
{
if (asyncResult == null || asyncResult.AsyncState == null)
{
Log("CallBack", "回调失败!!!");
return;
}
int result = (asyncResult.AsyncState as NewTaskDelegate).EndInvoke(asyncResult);
Log("CallBack", "任务完成,结果:" + result);
}
private delegate int NewTaskDelegate(int second, int millisecond);
private int newTask(int second, int millisecond)
{
Log("Task", "任务开始 线程休眠" + (second * 1000 + millisecond) + "毫秒");
Thread.Sleep(second * 1000 + millisecond);
System.Random random = new System.Random();
int n = random.Next(10000);
Log("Task", "任务完成");
return n;
}
private void Log(string threadName, string msg)
{
Debug.LogFormat("{0} [{1}] {2}", time, threadName, msg);
}
public string time
{
get
{
return DateTime.Now.ToString("mm:ss:ffff");
}
}
}
[out]
52:11:4363 [Main] BeginInvoke Before 52:11:4419 [Task] 任务开始 线程休眠2500毫秒 52:13:9475 [Task] 任务完成 52:13:9483 [CallBack] 任务完成,结果:9854
5、其他组件的BeginXXX和EndXXX方法
在其他的.net组件中也有类似BeginInvoke和EndInvoke的方法,如System.Net.HttpWebRequest类的BeginGetResponse和EndGetResponse方法。其使用方法类似于委托类型的BeginInvoke和EndInvoke方法,例如:
using UnityEngine;
using System.Collections;
using System;
using System.Net;
using System.IO;
public class TestThreadHttpWebRequest : MonoBehaviour {
public string url = "http://blog.ihaiu.com";
public void Test()
{
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url);
//异步请求
IAsyncResult asyncResult = request.BeginGetResponse(requestCompleted, request);
Log("Main", "任务开始");
}
//回调函数 此方法不是在Main线程运行
private void requestCompleted(IAsyncResult asyncResult)
{
if (asyncResult == null || asyncResult.AsyncState==null)
{
Log("Callback", "回调失败");
return;
}
HttpWebRequest hwr = asyncResult.AsyncState as HttpWebRequest;
HttpWebResponse response = (HttpWebResponse)hwr.EndGetResponse(asyncResult);
StreamReader sr = new StreamReader(response.GetResponseStream());
string str = sr.ReadToEnd();
Log("Callback","返回流长度:" + str.Length + " " + str);
}
private void Log(string threadName, string msg)
{
Debug.LogFormat("{0} [{1}] {2}", time, threadName, msg);
}
public string time
{
get
{
return DateTime.Now.ToString("mm:ss:ffff");
}
}
}
[out]
30:13:8687 [Main] 任务开始 30:14:0108 [Callback] 返回流长度:14278 <!DOCTYPE html><!--STATUS OK-->