Unity не безопасно Thread
, поэтому они решили сделать невозможным вызов своего API из другого Thread
путем добавления механизма для исключения исключения, когда его API используется из другого Thread
.
Этот вопрос задавали так много раз, но не было правильного решения / ответа ни на один из них. Ответы обычно «используют плагин» или делают что-то не потокобезопасное. Надеюсь, это будет последним.
Решение, которое вы обычно увидите на веб-сайте Stackoverflow или на веб-сайте Unity, - это просто использовать переменную boolean
, чтобы основной поток знал, что вам нужно выполнить код в основной Thread
. Это неверно, так как это не потокобезопасное и не дает вам контроля для предоставления функции вызова. Что делать, если у вас есть несколько Threads
, которые должны уведомить основной поток?
Другое решение, которое вы увидите, это использовать сопрограмму вместо Thread
. Это не работает. Использование сопрограммы для сокетов ничего не изменит. Вы все равно столкнетесь с проблемами замораживания . Вы должны придерживаться кода Thread
или использовать Async
.
Один из правильных способов сделать это - создать коллекцию, такую как List
. Когда вам нужно что-то выполнить в главном потоке, вызовите функцию, которая хранит код для выполнения в Action
. Скопируйте List
в Action
в локальный List
из Action
, затем выполните код из локального Action
в этом List
, затем очистите это List
. Это препятствует тому, чтобы другие Threads
не дождались завершения его выполнения.
Вам также нужно добавить volatile boolean
, чтобы уведомить функцию Update
, что в List
будет выполняться код. При копировании List
в локальный List
, который должен быть обернут вокруг ключевого слова lock
, чтобы предотвратить запись другого потока.
Сценарий, который выполняет то, что я упомянул выше:
UnityThread
Сценарий:
#define ENABLE_UPDATE_FUNCTION_CALLBACK
#define ENABLE_LATEUPDATE_FUNCTION_CALLBACK
#define ENABLE_FIXEDUPDATE_FUNCTION_CALLBACK
using System;
using System.Collections;
using UnityEngine;
using System.Collections.Generic;
public class UnityThread : MonoBehaviour
{
//our (singleton) instance
private static UnityThread instance = null;
////////////////////////////////////////////////UPDATE IMPL////////////////////////////////////////////////////////
//Holds actions received from another Thread. Will be coped to actionCopiedQueueUpdateFunc then executed from there
private static List actionQueuesUpdateFunc = new List();
//holds Actions copied from actionQueuesUpdateFunc to be executed
List actionCopiedQueueUpdateFunc = new List();
// Used to know if whe have new Action function to execute. This prevents the use of the lock keyword every frame
private volatile static bool noActionQueueToExecuteUpdateFunc = true;
////////////////////////////////////////////////LATEUPDATE IMPL////////////////////////////////////////////////////////
//Holds actions received from another Thread. Will be coped to actionCopiedQueueLateUpdateFunc then executed from there
private static List actionQueuesLateUpdateFunc = new List();
//holds Actions copied from actionQueuesLateUpdateFunc to be executed
List actionCopiedQueueLateUpdateFunc = new List();
// Used to know if whe have new Action function to execute. This prevents the use of the lock keyword every frame
private volatile static bool noActionQueueToExecuteLateUpdateFunc = true;
////////////////////////////////////////////////FIXEDUPDATE IMPL////////////////////////////////////////////////////////
//Holds actions received from another Thread. Will be coped to actionCopiedQueueFixedUpdateFunc then executed from there
private static List actionQueuesFixedUpdateFunc = new List();
//holds Actions copied from actionQueuesFixedUpdateFunc to be executed
List actionCopiedQueueFixedUpdateFunc = new List();
// Used to know if whe have new Action function to execute. This prevents the use of the lock keyword every frame
private volatile static bool noActionQueueToExecuteFixedUpdateFunc = true;
//Used to initialize UnityThread. Call once before any function here
public static void initUnityThread(bool visible = false)
{
if (instance != null)
{
return;
}
if (Application.isPlaying)
{
// add an invisible game object to the scene
GameObject obj = new GameObject("MainThreadExecuter");
if (!visible)
{
obj.hideFlags = HideFlags.HideAndDontSave;
}
DontDestroyOnLoad(obj);
instance = obj.AddComponent();
}
}
public void Awake()
{
DontDestroyOnLoad(gameObject);
}
//////////////////////////////////////////////COROUTINE IMPL//////////////////////////////////////////////////////
#if (ENABLE_UPDATE_FUNCTION_CALLBACK)
public static void executeCoroutine(IEnumerator action)
{
if (instance != null)
{
executeInUpdate(() => instance.StartCoroutine(action));
}
}
////////////////////////////////////////////UPDATE IMPL////////////////////////////////////////////////////
public static void executeInUpdate(System.Action action)
{
if (action == null)
{
throw new ArgumentNullException("action");
}
lock (actionQueuesUpdateFunc)
{
actionQueuesUpdateFunc.Add(action);
noActionQueueToExecuteUpdateFunc = false;
}
}
public void Update()
{
if (noActionQueueToExecuteUpdateFunc)
{
return;
}
//Clear the old actions from the actionCopiedQueueUpdateFunc queue
actionCopiedQueueUpdateFunc.Clear();
lock (actionQueuesUpdateFunc)
{
//Copy actionQueuesUpdateFunc to the actionCopiedQueueUpdateFunc variable
actionCopiedQueueUpdateFunc.AddRange(actionQueuesUpdateFunc);
//Now clear the actionQueuesUpdateFunc since we've done copying it
actionQueuesUpdateFunc.Clear();
noActionQueueToExecuteUpdateFunc = true;
}
// Loop and execute the functions from the actionCopiedQueueUpdateFunc
for (int i = 0; i < actionCopiedQueueUpdateFunc.Count; i++)
{
actionCopiedQueueUpdateFunc[i].Invoke();
}
}
#endif
////////////////////////////////////////////LATEUPDATE IMPL////////////////////////////////////////////////////
#if (ENABLE_LATEUPDATE_FUNCTION_CALLBACK)
public static void executeInLateUpdate(System.Action action)
{
if (action == null)
{
throw new ArgumentNullException("action");
}
lock (actionQueuesLateUpdateFunc)
{
actionQueuesLateUpdateFunc.Add(action);
noActionQueueToExecuteLateUpdateFunc = false;
}
}
public void LateUpdate()
{
if (noActionQueueToExecuteLateUpdateFunc)
{
return;
}
//Clear the old actions from the actionCopiedQueueLateUpdateFunc queue
actionCopiedQueueLateUpdateFunc.Clear();
lock (actionQueuesLateUpdateFunc)
{
//Copy actionQueuesLateUpdateFunc to the actionCopiedQueueLateUpdateFunc variable
actionCopiedQueueLateUpdateFunc.AddRange(actionQueuesLateUpdateFunc);
//Now clear the actionQueuesLateUpdateFunc since we've done copying it
actionQueuesLateUpdateFunc.Clear();
noActionQueueToExecuteLateUpdateFunc = true;
}
// Loop and execute the functions from the actionCopiedQueueLateUpdateFunc
for (int i = 0; i < actionCopiedQueueLateUpdateFunc.Count; i++)
{
actionCopiedQueueLateUpdateFunc[i].Invoke();
}
}
#endif
////////////////////////////////////////////FIXEDUPDATE IMPL//////////////////////////////////////////////////
#if (ENABLE_FIXEDUPDATE_FUNCTION_CALLBACK)
public static void executeInFixedUpdate(System.Action action)
{
if (action == null)
{
throw new ArgumentNullException("action");
}
lock (actionQueuesFixedUpdateFunc)
{
actionQueuesFixedUpdateFunc.Add(action);
noActionQueueToExecuteFixedUpdateFunc = false;
}
}
public void FixedUpdate()
{
if (noActionQueueToExecuteFixedUpdateFunc)
{
return;
}
//Clear the old actions from the actionCopiedQueueFixedUpdateFunc queue
actionCopiedQueueFixedUpdateFunc.Clear();
lock (actionQueuesFixedUpdateFunc)
{
//Copy actionQueuesFixedUpdateFunc to the actionCopiedQueueFixedUpdateFunc variable
actionCopiedQueueFixedUpdateFunc.AddRange(actionQueuesFixedUpdateFunc);
//Now clear the actionQueuesFixedUpdateFunc since we've done copying it
actionQueuesFixedUpdateFunc.Clear();
noActionQueueToExecuteFixedUpdateFunc = true;
}
// Loop and execute the functions from the actionCopiedQueueFixedUpdateFunc
for (int i = 0; i < actionCopiedQueueFixedUpdateFunc.Count; i++)
{
actionCopiedQueueFixedUpdateFunc[i].Invoke();
}
}
#endif
public void OnDisable()
{
if (instance == this)
{
instance = null;
}
}
}
ИСПОЛЬЗОВАНИЕ:
Эта реализация позволяет вам вызывать функции в 3 наиболее используемых функциях Unity: Update
, LateUpdate
и FixedUpdate
. Это также позволяет вам вызвать функцию сопротектора в главном Thread
. Он может быть расширен, чтобы иметь возможность вызывать функции в других функциях обратного вызова Unity, таких как OnPreRender
и OnPostRender
.
1. Сначала инициализируйте его из функции Awake()
.
void Awake()
{
UnityThread.initUnityThread();
}
2.Чтобы выполнить код в главном Thread
из другого потока:
UnityThread.executeInUpdate(() =>
{
transform.Rotate(new Vector3(0f, 90f, 0f));
});
Это повернет текущий объект, к которому привязан к 90 градусам. Теперь вы можете использовать Unity API (transform.Rotate
) в другом Thread
.
3.Чтобы вызвать функцию в главном Thread
из другого потока:
Action rot = Rotate;
UnityThread.executeInUpdate(rot);
void Rotate()
{
transform.Rotate(new Vector3(0f, 90f, 0f));
}
Образцы # 2 и # 3 выполняются в функции Update
.
4.Чтобы выполнить код в функции LateUpdate
от другого Тема:
Пример этого кода отслеживания камеры.
UnityThread.executeInLateUpdate(()=>
{
//Your code camera moving code
});
5.Чтобы выполнить код в функции FixedUpdate
из другого потока :
Пример этого при выполнении физических упражнений, таких как добавление силы к Rigidbody
.
UnityThread.executeInFixedUpdate(()=>
{
//Your code physics code
});
6.Чтобы запустить функцию сопрограммы в главном Thread
из другого потока :
UnityThread.executeCoroutine(myCoroutine());
IEnumerator myCoroutine()
{
Debug.Log("Hello");
yield return new WaitForSeconds(2f);
Debug.Log("Test");
}
Наконец, если вам не нужно ничего выполнять в функциях LateUpdate
и FixedUpdate
, вы должны прокомментировать обе строки этого кода ниже:
//#define ENABLE_LATEUPDATE_FUNCTION_CALLBACK
//#define ENABLE_FIXEDUPDATE_FUNCTION_CALLBACK
Это увеличит производительность.
Если вы хотите посчитать количество строк на дату, вы можете попробовать .N
foverlaps(df1, df2)[, .(mysum = .N), by = Date ]
Date mysum
1: 2018-01-01 7
2: 2018-01-08 7
3: 2018-01-15 7
4: 2018-01-22 7
5: 2018-01-29 2
Если вы хотите подсчитать количество уникальных значений на дату, вы можете попробовать uniqueN()
Оба .N
и uniqueN()
взяты из {data.table}
.
Вместо list(mySum = count(value))
попробуйте c(mySum = count(value))
. Кодекс работает для меня тогда.
d2 <- foverlaps(df1, df2)[, c(mySum = count(value)), by = Date ]