U3d 使用 异步 async await
1.1 下载 CSharp60Support 文件夹,里面包含需要的文件
百度网盘:
https://pan.baidu.com/s/1kVJfy6b 密码: 3pzf
2. 复制 CSharp60Support 这个文件夹到Unity项目。它应该放在项目的根目录(重要理解,不要放在Assets下) , Assets文件夹同级目录
3. 导入 CSharp60Support for Unity 5.unitypackage 到你的项目。这是位于 csharp60support 文件夹内
演示
using
UnityEngine;
using
System.Threading.Tasks;
//无返回值情况
class
Program
:
MonoBehaviour
{
void
Start()
{
ExecuteAsync();
//新建一个线程执行这个函数,不阻塞主线程,立即执行下面
Debug
.Log(
"start"
);
}
async
void
ExecuteAsync()
{
await
TaskEx
.Run(() =>
{
Debug
.Log(
"Async Executed"
);
});
Debug
.Log(
"End"
);
}
}
输出结果:
Debug
.Log(
"start"
);
Debug
.Log(
"Async Executed"
);
Debug
.Log(
"End"
);
using
UnityEngine;
using
System.Threading.Tasks;
//有返回参数阻塞式
class
Program1
:
MonoBehaviour
{
void
Start()
{
var
t = ExecuteAsync();
//新建一个线程执行这个函数
t.Wait();
//阻塞,一直等待函数执行完成
Debug
.Log(
"start"
);
string
result =t.Result;
//返回值
}
async
Task
<
string
> ExecuteAsync()
{
await
TaskEx
.Run(() =>
{
Debug
.Log(
"Async Executed"
);
});
Debug
.Log(
"End"
);
return
"return Executed"
;
}
}
输出结果:
Debug
.Log(
"Async Executed"
);
Debug
.Log(
"End"
);
Debug
.Log(
"start"
);
using
UnityEngine;
using
System.Threading.Tasks;
public
class
Testt2
:
MonoBehaviour
{
void
Start ()
{
YhyTools
.LogTime(
"Test2 start join"
);
var
t = ShowRandomImages();
t.Wait();
string
result =t.Result;
//返回值
YhyTools
.LogTime(
"Test2 start end"
);
}
private
async
Task
<
string
> ShowRandomImages()
{
for
(
int
i = 0; i < 9; i++)
{
await
TaskEx
.Delay(1000);
//延迟一秒
YhyTools
.LogTime(
"ShowRandomImages"
);
}
return
"1"
;
}
}
输出结果:
主要的类 Task
TaskEx
AsyncTools
await
0;
//等待指定的秒数,如果参数是负数或者0 等一帧。参数可以是 int float
await
TaskEx
.Delay(100);//开始一个任务,指定时间后完成
await
AsyncTools
.ToThreadPool();//切换到后台线程的执行。
await
AsyncTools
.ToLateUpdate();//切换主线程的更新上下文的执行。
.ToFixedUpdate();
.
ToUpdate();
下面是官网例子
using
System;
using
System.Reflection;
using
UnityEngine;
using
UnityEngine.UI;
public
class
CrossPlatformTest
:
MonoBehaviour
{
private
void
Start()
{
Type
type =
null
;
foreach
(
var
assembly
in
AppDomain
.CurrentDomain.GetAssemblies())
{
type = assembly.GetType(
"System.Threading.Platform"
);
if
(type !=
null
)
{
break
;
}
}
var
methodInfo = type.GetMethod(
"Yield"
,
BindingFlags
.Static |
BindingFlags
.NonPublic);
ShowResult(
false
);
methodInfo.Invoke(
null
,
null
);
ShowResult(
true
);
}
private
static
void
ShowResult(
bool
success)
{
Camera
.main.backgroundColor = success ?
Color
.green :
Color
.magenta;
var
textObject = FindObjectOfType<
Text
>();
textObject.text = success ?
"SUCCESS"
:
"FAIL"
;
}
}
#if
UNITY_5
using
UnityEngine;
using
UnityEngine.EventSystems;
using
UnityEngine.Networking;
public
class
AsyncOperationAwaiterTest
:
MonoBehaviour
,
IPointerClickHandler
{
private
Texture
originalTexture;
private
Texture2D
texture;
private
Material
material;
public
async
void
OnPointerClick(
PointerEventData
eventData)
{
Debug
.Log(
"downloading..."
);
var
request =
UnityWebRequest
.Get(
"http://placeimg.com/512/512"
);
await
request.Send();
Debug
.Log(
"downloaded "
+ request.downloadedBytes +
" bytes"
);
texture.LoadImage(request.downloadHandler.data,
true
);
material.mainTexture = texture;
}
private
void
Start()
{
material = GetComponent<
Renderer
>().sharedMaterial;
originalTexture = material.mainTexture;
texture =
new
Texture2D
(512, 512);
Debug
.Log(
"
\n
--> Click on the box to change its texture <--
\n
"
);
}
private
void
OnDestroy()
{
Destroy(texture);
material.mainTexture = originalTexture;
}
private
void
Update()
{
transform.Rotate(0, 90 *
Time
.deltaTime, 0);
}
}
#endif
using
UnityEngine;
using
UnityEngine.UI;
internal
class
Logger
:
MonoBehaviour
{
private
Text
textControl;
private
void
Awake()
{
textControl = GetComponent<
Text
>();
#if
UNITY_5
Application
.logMessageReceivedThreaded += Application_logMessageReceived;
#else
Application.RegisterLogCallback(Application_logMessageReceived);
#endif
Debug
.Log(
"<color=red>Current platform: "
+
Application
.platform +
"</color>
\n
"
);
}
private
void
Application_logMessageReceived(
string
message,
string
stackTrace,
LogType
type)
{
textControl.text += message +
"
\n
"
;
}
}
using
System;
using
System.Threading;
using
System.Threading.Tasks;
using
UnityEngine;
using
UnityEngine.UI;
using
Random
= UnityEngine.
Random
;
public
class
SlideshowDemo
:
MonoBehaviour
{
public
RawImage
rawImage;
public
Text
counterText;
public
Button
startButton;
public
Button
abortButton;
[
Range
(.1f, 5)]
public
float
delayInSeconds = 1;
private
Texture2D
texture;
private
CancellationTokenSource
tokenSource;
private
void
Start()
{
texture =
new
Texture2D
(320, 200);
rawImage.texture = texture;
}
private
void
Destroy()
{
Destroy(texture);
}
public
async
void
StartSlideshow()
// async methods can be on-click handlers, coroutines can't
{
tokenSource =
new
CancellationTokenSource
();
startButton.interactable =
false
;
abortButton.interactable =
true
;
try
{
Debug
.Log(
"Slideshow started"
);
await
ShowRandomImages(
Random
.Range(2, 21), tokenSource.Token);
Debug
.Log(
"Run to completion"
);
}
catch
(
TaskCanceledException
)
{
Debug
.Log(
"Aborted"
);
}
finally
{
startButton.interactable =
true
;
abortButton.interactable =
false
;
}
}
public
void
AbortSlideshow()
{
abortButton.interactable =
false
;
tokenSource.Cancel();
}
private
async
Task
ShowRandomImages(
int
count,
CancellationToken
cancellationToken)
{
for
(
int
i = 0; i < count; i++)
{
var
image =
await
AsyncTools
.DownloadAsBytesAsync(
"http://placeimg.com/320/200"
, cancellationToken);
texture.LoadImage(image);
rawImage.SetNativeSize();
counterText.text =
$"
{i + 1}
of
{count}
"
;
if
(i != count - 1)
{
await
TaskEx
.Delay(
TimeSpan
.FromSeconds(delayInSeconds), cancellationToken);
}
}
}
}
using
System.Threading.Tasks;
using
UnityEngine;
internal
class
ThreadPingPongDemo
:
MonoBehaviour
{
public
async
void
AsyncAwaitEventHandler()
{
AsyncTools
.WhereAmI(
"1"
);
// main thread, Update context
var
task1 =
Task
.Factory.StartNew(() =>
AsyncTools
.WhereAmI(
"2"
));
// background thread
var
task2 =
new
Task
(() =>
AsyncTools
.WhereAmI(
"3"
));
// main thread, FixedUpdate context
task2.Start(
UnityScheduler
.FixedUpdateScheduler);
var
task3 =
Task
.Factory.StartNew(() =>
AsyncTools
.WhereAmI(
"4"
));
// background thread
// returns execution of asynchronous method to the main thread,
// if it was originally called from the main thread
await
TaskEx
.WhenAll(task1, task2, task3);
AsyncTools
.WhereAmI(
"5"
);
// main thread, Update context
await
TaskEx
.Delay(100).ConfigureAwait(
false
);
AsyncTools
.WhereAmI(
"6"
);
// can be any thread, since the previous line states that we don't care
Debug
.Log(
"done"
);
}
public
async
void
TaskContinueWithEventHandler()
{
AsyncTools
.WhereAmI(
"1"
);
// main thread
var
originalTask =
new
Task
(() =>
AsyncTools
.WhereAmI(
"2"
));
var
continuationTask1 = originalTask.ContinueWith(
previousTask =>
AsyncTools
.WhereAmI(
"3"
),
UnityScheduler
.UpdateScheduler);
// main thread, Update context
var
continuationTask2 = continuationTask1.ContinueWith(
previousTask =>
AsyncTools
.WhereAmI(
"4"
));
// background thread
var
continuationTask3 = continuationTask2.ContinueWith(
previousTask =>
AsyncTools
.WhereAmI(
"5"
),
UnityScheduler
.FixedUpdateScheduler);
// main thread, FixedUpdate context
var
continuationTask4 = continuationTask3.ContinueWith(
previousTask =>
AsyncTools
.WhereAmI(
"6"
));
// background thread
originalTask.Start(
UnityScheduler
.ThreadPoolScheduler);
// start the task chain from a background thread
await
continuationTask4;
Debug
.Log(
"done"
);
}
public
void
TaskRunSynchronouslyFromMainThreadEventHandler()
{
Debug
.Log(
"Launched from the main thread..."
);
RunTasksSynchronously();
Debug
.Log(
"done"
);
}
public
async
void
TaskRunSynchronouslyFromBackgroundThreadEventHandler()
{
Debug
.Log(
"Launched from a background thread..."
);
await
Task
.Factory.StartNew(RunTasksSynchronously);
Debug
.Log(
"done"
);
}
private
void
RunTasksSynchronously()
{
/*
Fact #1: ThreadPoolScheduler supports running tasks on any thread.
Fact #2: MainThreadScheduler supports running tasks on the main thread only.
If this method is called from the main thread, all the tasks will be executed on the main thread.
If this method is called from a background thread, the tasks associated with the thread pool scheduler
will be executed on the current background thread, and the tasks associated with the main thread scheduler will be
executed on the main thread while the background thread will be blocked until the execution completes.
*/
/* ATTENTION!!! ВНИМАНИЕ!!! ¡¡¡ATENCIÓN!!!
Using UpdateScheduler, LateUpdateScheduler or FixedUpdateScheduler for running
tasks synchronously from the main thread will cause a DEADLOCK if the current
context doesn't match the task scheduler's type.
E.g. don't call task.RunSynchronously(UnityScheduler.FixedUpdateScheduler) from
the Update method.
*/
AsyncTools
.WhereAmI(
"1"
);
var
task =
new
Task
(() =>
AsyncTools
.WhereAmI(
"2"
));
task.RunSynchronously(
UnityScheduler
.UpdateScheduler);
task =
new
Task
(() =>
AsyncTools
.WhereAmI(
"3"
));
task.RunSynchronously(
UnityScheduler
.ThreadPoolScheduler);
task =
new
Task
(() =>
AsyncTools
.WhereAmI(
"4"
));
task.RunSynchronously(
UnityScheduler
.UpdateScheduler);
task =
new
Task
(() =>
AsyncTools
.WhereAmI(
"5"
));
task.RunSynchronously();
// no scheduler => use default, which, in this case, is ThreadPoolScheduler
task =
new
Task
(() =>
AsyncTools
.WhereAmI(
"6"
));
task.RunSynchronously(
UnityScheduler
.UpdateScheduler);
}
public
async
void
ContextSwitchFromMainThreadEventHandler()
{
Debug
.Log(
"Launched from the main thread..."
);
await
TestContextSwitch();
Debug
.Log(
"done"
);
}
public
async
void
ContextSwitchFromBackgroundThreadEventHandler()
{
Debug
.Log(
"Launched from a background thread..."
);
await
Task
.Factory.StartNew(
async
() =>
await
TestContextSwitch()).Unwrap();
Debug
.Log(
"done"
);
}
private
static
async
Task
TestContextSwitch()
{
AsyncTools
.WhereAmI(
"1"
);
await
AsyncTools
.ToThreadPool();
AsyncTools
.WhereAmI(
"2"
);
await
0;
AsyncTools
.WhereAmI(
"3"
);
await
AsyncTools
.ToUpdate();
AsyncTools
.WhereAmI(
"4"
);
await
0;
AsyncTools
.WhereAmI(
"5"
);
await
AsyncTools
.ToLateUpdate();
AsyncTools
.WhereAmI(
"6"
);
await
0;
AsyncTools
.WhereAmI(
"7"
);
await
AsyncTools
.ToFixedUpdate();
AsyncTools
.WhereAmI(
"8"
);
await
0;
AsyncTools
.WhereAmI(
"9"
);
}
}