#はじめに
生じた問題はタイトルのとおりです.
Arduino(私の場合はTeensy4.0)からUnityにシリアルでデータを送る際,有線だとUnity終了時にSerialPort.Close()が機能したのですが,Bluetooth経由だと機能せずにアプリケーション終了後もゾンビスレッドが爆誕してしまった.といった感じです
#環境
Windows10
Unity2019.4.3f1
マイコン : Teensy4.0
Bluetoothモジュール : BlueSMiRF Gold
#使用スクリプト
Unity-Arduino間のシリアル通信であまりにも有名なスクリプトを用いています.
今回は一昔前のThreadを使用したものではなく,Taskを用いた以下の記事を参考にさせていただいています.
[Unity]非同期処理(Task)の利用例(UDP送受信,SerialPortのRead)
#原因
なぜSerialPort.Close()
が失敗するかですが,それはSerialPort.Close()
を呼ぶタイミングでも裏で走っている以下のReadAsync()
タスク内のSerialPort.ReadLine()
が終了していないからです.
もっと正確に言うと,SerialPort.Readline()
でシリアルからの入力を待ち続けてしまっており,そのときにSerial.Close()
を呼び出してしまうとこのTaskがゾンビTaskになってアプリケーション終了後も走り続けてしまう,といった感じでしょうか...(Unityを落とせばこのTaskも消えます.)
async Task ReadAsync()
{
await Task.Run(() => {
while (isRunning && serialPort != null && serialPort.IsOpen)
{
try
{
string message = serialPort.ReadLine();
OnDataReceived.Invoke(message);
}
catch (System.Exception e)
{
Debug.LogWarning(e.Message);
}
}
});
}
何も入力がないときはSerialPort.ReadLine()
が終了するように,Serial.Open()
時に以下のようにタイムアウトを設定してあげましょう.(タイムアウトの間隔はアプリに合わせて変えてあげましょう.)
public void Open(string portName)
{
if(isRunning) return;
currentPortName = portName;
serialPort = new SerialPort(portName, baudRate, Parity.None, 8, StopBits.One);
// ここでReadのタイムアウトを設定する.
serialPort.ReadTimeout = 500; //[milliseconds]
serialPort.Open();
isRunning = true;
}
これでSerial.Close()
がちゃんと機能し,ゾンビタスクが爆誕することはなくなりました.
なぜ有線だとこのタイムアウトの設定がいらなくてBluetoothだと必要なのかが未だにわかっていませんが,とりあえず良しとします.