1

所以我试图执行一个UnityWebrequest,它需要从一个协程开始。现在尝试在某个时候调试所有内容,我发现从 Start 函数调用 Coroutine 是可行的(接收 Debug.Log 消息),但是从普通函数调用它将不起作用(我没有收到 内的调试消息GetRequest)。

因此我认为由于某种原因协程没有在主线程上运行(因为也没有出现错误)。

有人知道我如何强制协程在主线程上运行或解决我的问题吗?

在这里,您将找到WebGet从不在 Monobehaviour 中的类调用的代码,但包含的类WebGet & GetRequest是。

(请忽略其中的请求变量,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);
            }

        }
4

1 回答 1

1

大多数 Unity API 只能在 Unity 主线程上使用,StartCoroutine就是其中之一。

或我的问题的其他解决方案

如果您已经在后台线程上,您也可以在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不会延迟调用它的方法(这正是我们希望通过使用协程来避免的),而是立即继续使用其余代码。

你当然可以做类似的事情

var request = UnityWebRequest.Get(url);
request.SendWebRequest();
return request;

不使用yield它并且在任何协程之外,但是在您继续访问和使用结果之前,您需要确保这request.isDone一点。true


如何强制协程在主线程上运行?

为了强制在主线程上发生事情,您可以使用一种通常称为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));
});
于 2022-02-08T09:55:21.190 回答