【问题标题】:Main thread coroutine using Queue<Action>使用 Queue<Action> 的主线程协程
【发布时间】:2014-12-16 09:30:46
【问题描述】:

当我从 websockets 获取一些数据并尝试通过协程显示它时,我遇到了一些麻烦。

首先,我有一个 classA 附加到一个打开 websocket 并显示我收到的数据的对象:

public class ClassA : MonoBehaviour {

     ...

     public IEnumerator ConnectWebSocket(url)
     {
       // in the websocket class, start the websocket connection, which
       // will return data through a callback inside the return string
       WebSocketClass.WebSocketStart(url, delegate(string result)
       {
         // receive websocket data and call the functions that displays it
         WebSocketData(result);
       });

       // wait for the socket connection
       while (WebSocketClass.WebSocketGetState() == WebSocketSharp.WebSocketState.CONNECTING)
       {
         yield return 0;
       }

       if (WebSocketClass.WebSocketGetState() == WebSocketSharp.WebSocketState.OPEN)
       {
          break;
       }

       ...

    }

     // function that gets websocket data and starts couroutine to display it
     public void WebSocketData(string data)
     {
       StartCoroutine(DisplayMessage(data));       
     }
}

但 Unity 抱怨下一个错误:

StartCoroutine_Auto 只能被调用 从主线程。构造函数和 字段初始化器将被执行 加载时从加载线程 场景。请勿在 构造函数或字段初始值设定项, 而是将初始化代码移动到 唤醒或启动功能。

我在unity论坛搜索,找到了这个解决方案:

public class ClassA : MonoBehaviour {

  ...

  public IEnumerator ConnectWebSocket(url)
  {
    // in the websocket class, start the websocket connection, which
    // will return data through a callback inside the return string
    WebSocketClass.WebSocketStart(url, delegate(string result)
    {
      // receive websocket data and call the functions that displays it
      WebSocketData(result);
    });

    // wait for the socket connection
    while (WebSocketClass.WebSocketGetState() == WebSocketSharp.WebSocketState.CONNECTING)
    {
      yield return 0;
    }

    if (WebSocketClass.WebSocketGetState() == WebSocketSharp.WebSocketState.OPEN)
    {
       break;
    }

    ...

  }

  // function that gets websocket data and starts couroutine to display it
  public void WebSocketData(string data)
  {
    DoOnMainThread.ExecuteOnMainThread.Enqueue(() => { StartCoroutine(DisplayMessage(data)); });    
  }
}

// class to manage the websocket data display inside the main thread
public class DoOnMainThread : MonoBehaviour 
{

  public readonly static Queue<Action> ExecuteOnMainThread = new Queue<Action>();

  public virtual void Update()
  {
    // dispatch stuff on main thread
    while (ExecuteOnMainThread.Count > 0)
    {
      ExecuteOnMainThread.Dequeue().Invoke();
    }
  }
}

而且它有效!问题是即使我在同一个cs文件中编写了两个类并附加到一个对象,当我改变场景,返回那个场景,并从websocket接收任何数据时,会显示下一个错误:

MissingReferenceException:对象 'ClassA' 类型的已被销毁 但您仍在尝试访问它。 您的脚本应该检查它是否 为空,否则您不应销毁 目的。 UnityEngine.MonoBehaviour.StartCoroutine (IEnumerator 例程)(在 C:/BuildAgent/work/d63dfc6385190b60/artifacts/EditorGenerated/UnityEngineMonoBehaviour.cs:62)

如文档所述,我尝试在加载新场景时不破坏对象:

void Awake()
    {
        DontDestroyOnLoad(transform.gameObject);
    }

但错误仍然出现。

奇怪的是,虽然有错误,但是从websocket接收到的数据显示没有任何问题。

有人知道如何避免这个问题吗?有什么方法可以在不使用第二个类的情况下触发主线程内的协程?或其他解决方案来避免此错误?

谢谢!

【问题讨论】:

  • ExecuteOnMainThread 的访问应该在lock 部分中(使用ExecuteOnMainThread 作为保护)。

标签: c# multithreading unity3d websocket coroutine


【解决方案1】:

我发现了问题:

public readonly static Queue<Action> ExecuteOnMainThread = new Queue<Action>();

它是静态的,所以当一个公共类被实例化并生成另一个ExecuteOnMainThread时就会出现问题。

所以只需删除“静态”并使其在每次由 Unity 创建 ClassA 时自行销毁并生成。

现在它就像一个魅力:)

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2020-07-02
    • 1970-01-01
    • 2023-03-04
    • 2020-05-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多