Hi there,
I have written a small program where a capsule moves from one quad to the other and upon reaching one; it stops and rotates to face the next quad and then moves towards it.
I decided to make the program multi threaded as I wanted to test multi threading in unity. I have used the thread signaling methodology using EventWaitHandles to establish a communication between 2 child threads and the main thread. The code runs fine so far with some resources being shared among different threads but I cant tell for sure if the program is thread safe or not.
I know that debugging multi threaded programs are a nightmare and unfortunately there isn’t a better way to debug these kinds of programs in unity without “Debug.Log()/print()” statements and afaik visual studio doesn’t have a thread debugger for unity as it does for conventional C# programs.
Therefore, I need some help to determine if my code is thread safe or not.
Thanks
using UnityEngine;
using System.Collections;
using System.Threading;
public class Dummy : MonoBehaviour
{
static EventWaitHandle ewh1 = new EventWaitHandle(false, EventResetMode.AutoReset);
static EventWaitHandle ewh2 = new EventWaitHandle(false, EventResetMode.AutoReset);
Thread t1, t2;
[SerializeField]
Transform waypoints;
Vector3[] wayPointQuads;
Vector3 capsulePosition;
bool stopThread;
bool gotNewData;
int index = 0;
[SerializeField]
float moveSpeed, rotateSpeed;
float turnAngle;
private void Start()
{
capsulePosition = transform.position;
SetUpArray();
t1 = new Thread(SetWayPointData);
t2 = new Thread(NotifyMainThread);
index = 0;
t1.Start();
t2.Start();
StartCoroutine("StartThread");
}
private void Update()
{
capsulePosition = transform.position;
if (gotNewData)
{
RotateCapsule();
ewh2.Set();
}
else transform.position += transform.forward * Time.deltaTime * moveSpeed;
}
void SetWayPointData()
{
ewh1.WaitOne();
while (true)
{
if (stopThread) break;
float distance = Vector3.Distance(capsulePosition, wayPointQuads[index % wayPointQuads.Length]);
if (distance < 0.3f)
{
index++;
Vector3 nextQuadDirection = (wayPointQuads[index % wayPointQuads.Length] - capsulePosition).normalized;
turnAngle = 90 - Mathf.Atan2(nextQuadDirection.z, nextQuadDirection.x) * Mathf.Rad2Deg;
WaitHandle.SignalAndWait(ewh2, ewh1);
}
}
}
void NotifyMainThread()
{
ewh2.WaitOne();
while (true)
{
if (stopThread) break;
WaitHandle.SignalAndWait(ewh1, ewh2);
gotNewData = true;
ewh2.WaitOne();
}
}
IEnumerator StartThread()
{
for (int i = 0; i < 2; i++) yield return new WaitForEndOfFrame();
ewh2.Set();
}
void SetUpArray()
{
wayPointQuads = new Vector3[4];
int i = 0;
foreach (Transform t in waypoints)
{
wayPointQuads[i] = t.position;
i++;
}
}
private void RotateCapsule()
{
float angleBetween = Vector3.Angle((wayPointQuads[index % wayPointQuads.Length] - transform.position).normalized, transform.forward);
if (angleBetween <= 1) gotNewData = false;
else
{
Vector3 targetDir = wayPointQuads[index % wayPointQuads.Length] - transform.position;
Vector3 newDir = Vector3.RotateTowards(transform.forward, targetDir, rotateSpeed * Time.deltaTime, 0.0f);
transform.rotation = Quaternion.LookRotation(newDir);
}
}
private void OnDisable()
{
StopAllThreads();
}
private void OnDestroy()
{
StopAllThreads();
}
private void OnApplicationQuit()
{
StopAllThreads();
}
void StopAllThreads()
{
ewh1.Set();
ewh2.Set();
stopThread = true;
}
}