●参考URL
http://mslgt.hatenablog.com/entry/2017/10/08/091841
https://qiita.com/toRisouP/items/a2c1bb1b0c4f73366bc6
■SynchronizationContext
SynchronizationContext は、スレッドごとにインスタンスを持っているとの事
SynchronizationContext.Currentすると現在のスレッドを取得できる
■CancellationTokenSource
別スレッド動作しているタスクの処理をキャンセルするために使用する。
■メインスレッドに処理を返す
Unity関係のクラスを操作しようと思うとメインスレッド以外では使用できないので。
下部にあるコードのように、UnityのメインスレッドでSynchronizationContext.Currentを使ってカレントスレッドを保持しておき
別スレッドからはPostでメインスレッド処理。
■Taskの処理を外からキャンセル。
CancellationTokenSource tokenSource = new CancellationTokenSource(); を定義
tokenSource.Cancel() を外側の処理で実行し、
Task内でtokenSource.Token.IsCancellationRequested にて判定する
■テストコード
using UnityEngine;
using UnityEngine.UI;
using System.Threading;
using System.Threading.Tasks;
public class TaskTest : MonoBehaviour {
// MainThreadのコンテキストを取得.
SynchronizationContext context = null;
CancellationTokenSource tokenSource = new CancellationTokenSource();
[SerializeField]
private Text TestLabel;
public void OnClickAwaitButton()
{
Debug.Log("OnClickAwaitButton() S");
// MainThreadのコンテキストを取得.
context = SynchronizationContext.Current;
Work();
Debug.Log("OnClickAwaitButton() E");
}
/// <summary>
/// async・awaitを使ってTaskを処理する
/// </summary>
/// <returns></returns>
async Task Work()
{
Debug.Log("実行開始");
await Task.Run(() =>
{
MethodC();
MethodD();
EndMethod();
});
Debug.Log("実行終了");
}
void MethodC()
{
for (int i = 0; i < 10000; i++)
{
Debug.Log("MethodC=" + i.ToString());
// Taskのキャンセルチェック。
if (tokenSource.Token.IsCancellationRequested)
{
Debug.Log("MethodC Cancel");
return;
}
// メインスレッド側の処理を行う
context.Post((state) =>
{
TestLabel.text = i.ToString();
}
, null);
}
}
void MethodD()
{
for (int j = 10000; j < 20000; j++)
{
Debug.Log("MethodD=" + j.ToString());
// Taskのキャンセルチェック。
if (tokenSource.Token.IsCancellationRequested)
{
Debug.Log("MethodD Cancel");
return;
}
// メインスレッド側の処理を行う
context.Post((state) =>
{
TestLabel.text = j.ToString();
}
, null);
}
}
void EndMethod()
{
Debug.Log("EndMethod");
// Taskのキャンセルチェック。
if (tokenSource.Token.IsCancellationRequested)
{
Debug.Log("EndMethod() Cancel");
return;
}
// メインスレッド側の処理を行う
context.Post((state) =>
{
TestLabel.text = "終了!";
}
, null);
}
/// <summary>
/// キャンセルボタン
/// </summary>
public void OnClickCancelButton()
{
Debug.Log("OnClickCancelButton() S");
// トークンを使って、Taskをキャンセルする。
tokenSource.Cancel();
Debug.Log("OnClickCancelButton() E");
}
}