在主线程中启动协程
Starting Coroutine in Main Thread
所以我想执行一个UnityWebrequest,它需要从Coroutine开始。
现在尝试在某个时候调试所有内容我发现从 Start 函数调用 Coroutine 是有效的(接收 Debug.Log 消息),但是从普通函数调用它它将不起作用(我没有收到调试消息) GetRequest
).
因此我认为由于某种原因协程没有运行在主线程上运行(因为也没有出现错误)。
有人知道我如何在主线程上强制执行协程 运行 或解决我的问题的其他方法吗?
在这里您会找到代码,其中 WebGet
从不在 Monobehaviour 中的 class 调用,但包含 WebGet & GetRequest
的 class 是。
(请忽略 WebGet
中的请求变量,这将在稍后阶段使用。
public UnityWebRequest WebGet(string url)
{
Debug.Log("This is the URL Get " + url);
var request = UnityWebRequest.Get(url);
Debug.Log("Request URL: " + request.url);
StartCoroutine(GetRequest(url));
Debug.Log("After Coroutine");
return request;
}
private IEnumerator GetRequest(string url)
{
var request = UnityWebRequest.Get(url);
Debug.Log("This is the URL inside coroutine " + url);
yield return request.SendWebRequest();
if (request.isNetworkError)
{
Debug.Log("Error While Sending: " + request.error);
}
else
{
Debug.Log("Received: " + request.result);
}
}
大部分UnityAPI只能在Unitymain-thread上使用,StartCoroutine
就是其中之一。
or another solution to my issue
如果您已经在后台线程上,您也可以简单地使用 UnityWebRequest.Get
而不使用任何协程,然后等待使用例如
request.SendWebRequest();
while(!request.isDone)
{
// depends on your use case, but in general for the love of your CPU
// you do want to wait a specific amount that is "ok" as a delay
// you could of curse reduce this to approximately frame wise check by using e.g. 17 ms (=> checks 60 times per second)
Thread.Sleep(50); // checks 20 times per second
}
if (request.isNetworkError)
{
Debug.Log("Error While Sending: " + request.error);
}
else
{
Debug.Log("Received: " + request.result);
}
请记住,您仍在后台线程中,此时可能不允许其他 Unity API 调用。
如果您的方法通常不在后台线程上,请注意 StartCoroutine
不会 NOT 延迟调用它的方法(这正是我们想要避免的通过使用协同程序),而是立即继续您的其余代码。
你当然可以这样做
var request = UnityWebRequest.Get(url);
request.SendWebRequest();
return request;
没有 yield
在任何协程之外对其进行处理,但是在您继续访问和使用结果之前,您需要确保 request.isDone
是 true
。
How I can enforce a Coroutine to run on the main-thread?
为了强制在主线程上发生事情,您可以使用通常称为 Main thread dispatcher 的模式。简化版本可能类似于
public class MainThreadDispatcher : MonoBehaviour
{
#region Singleton pattern (optional)
private static MainThreadDispatcher _instance;
public static MainThreadDispatcher Instance => _instance;
private void Awake()
{
if(_instance && _instance != this)
{
Destroy(gameObject);
return;
}
_instance = this;
DontDestroyOnLoad(gameObject);
}
#endregion Singleton
#region Dispatcher
// You use a thread-safe collection first-in first-out so you can pass on callbacks between the threads
private readonly ConcurrentQueue<Action> mainThreadActions = new ConcurrentQueue<Action>();
// This now can be called from any thread/task etc
// => dispatched action will be executed in the next Unity Update call
public void DoInMainThread(Action action);
{
mainThreadActions.Enqueue(action);
}
// In the Unity main thread Update call routine you work off whatever has been enqueued since the last frame
private void Update()
{
while(mainThreadActions.TryDequeue(out var action))
{
action?.Invoke();
}
}
#endregion Dispatcher
}
然后在你的情况下你可以使用
//MainThreadDispatcher.Instance.DoInMainThread(() =>
yourMainThreadDispatcherReference.DoInMainThread(() =>
{
StartCoroutine(GetRequest(url));
});
所以我想执行一个UnityWebrequest,它需要从Coroutine开始。
现在尝试在某个时候调试所有内容我发现从 Start 函数调用 Coroutine 是有效的(接收 Debug.Log 消息),但是从普通函数调用它它将不起作用(我没有收到调试消息) GetRequest
).
因此我认为由于某种原因协程没有运行在主线程上运行(因为也没有出现错误)。
有人知道我如何在主线程上强制执行协程 运行 或解决我的问题的其他方法吗?
在这里您会找到代码,其中 WebGet
从不在 Monobehaviour 中的 class 调用,但包含 WebGet & GetRequest
的 class 是。
(请忽略 WebGet
中的请求变量,这将在稍后阶段使用。
public UnityWebRequest WebGet(string url)
{
Debug.Log("This is the URL Get " + url);
var request = UnityWebRequest.Get(url);
Debug.Log("Request URL: " + request.url);
StartCoroutine(GetRequest(url));
Debug.Log("After Coroutine");
return request;
}
private IEnumerator GetRequest(string url)
{
var request = UnityWebRequest.Get(url);
Debug.Log("This is the URL inside coroutine " + url);
yield return request.SendWebRequest();
if (request.isNetworkError)
{
Debug.Log("Error While Sending: " + request.error);
}
else
{
Debug.Log("Received: " + request.result);
}
}
大部分UnityAPI只能在Unitymain-thread上使用,StartCoroutine
就是其中之一。
or another solution to my issue
如果您已经在后台线程上,您也可以简单地使用 UnityWebRequest.Get
而不使用任何协程,然后等待使用例如
request.SendWebRequest();
while(!request.isDone)
{
// depends on your use case, but in general for the love of your CPU
// you do want to wait a specific amount that is "ok" as a delay
// you could of curse reduce this to approximately frame wise check by using e.g. 17 ms (=> checks 60 times per second)
Thread.Sleep(50); // checks 20 times per second
}
if (request.isNetworkError)
{
Debug.Log("Error While Sending: " + request.error);
}
else
{
Debug.Log("Received: " + request.result);
}
请记住,您仍在后台线程中,此时可能不允许其他 Unity API 调用。
如果您的方法通常不在后台线程上,请注意 StartCoroutine
不会 NOT 延迟调用它的方法(这正是我们想要避免的通过使用协同程序),而是立即继续您的其余代码。
你当然可以这样做
var request = UnityWebRequest.Get(url);
request.SendWebRequest();
return request;
没有 yield
在任何协程之外对其进行处理,但是在您继续访问和使用结果之前,您需要确保 request.isDone
是 true
。
How I can enforce a Coroutine to run on the main-thread?
为了强制在主线程上发生事情,您可以使用通常称为 Main thread dispatcher 的模式。简化版本可能类似于
public class MainThreadDispatcher : MonoBehaviour
{
#region Singleton pattern (optional)
private static MainThreadDispatcher _instance;
public static MainThreadDispatcher Instance => _instance;
private void Awake()
{
if(_instance && _instance != this)
{
Destroy(gameObject);
return;
}
_instance = this;
DontDestroyOnLoad(gameObject);
}
#endregion Singleton
#region Dispatcher
// You use a thread-safe collection first-in first-out so you can pass on callbacks between the threads
private readonly ConcurrentQueue<Action> mainThreadActions = new ConcurrentQueue<Action>();
// This now can be called from any thread/task etc
// => dispatched action will be executed in the next Unity Update call
public void DoInMainThread(Action action);
{
mainThreadActions.Enqueue(action);
}
// In the Unity main thread Update call routine you work off whatever has been enqueued since the last frame
private void Update()
{
while(mainThreadActions.TryDequeue(out var action))
{
action?.Invoke();
}
}
#endregion Dispatcher
}
然后在你的情况下你可以使用
//MainThreadDispatcher.Instance.DoInMainThread(() =>
yourMainThreadDispatcherReference.DoInMainThread(() =>
{
StartCoroutine(GetRequest(url));
});