使用Queue <action> </action>的主线程协同程序

时间:2014-10-21 08:17:12

标签: c# multithreading unity3d websocket coroutine

当我从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   从主线程。建设者和   字段初始值设定项将被执行   从加载线程加载时   现场。不要在中使用此功能   构造函数或字段初始值设定项,   而是将初始化代码移动到   唤醒或启动功能。

我在团结论坛上搜索并找到了这个解决方案:

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:对象     类型&#39; ClassA&#39;已被摧毁     但你仍然试图访问它。     你的脚本应该检查它是否     是null或你不应该破坏     宾语。     UnityEngine.MonoBehaviour.StartCoroutine     (IEnumerator例程)(at     C:/BuildAgent/work/d63dfc6385190b60/artifacts/EditorGenerated/UnityEngineMonoBehaviour.cs:62)

  

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

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

但错误仍然存​​在。

奇怪的是,尽管存在错误,但显示从websocket接收的数据没有任何问题。

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

谢谢!

1 个答案:

答案 0 :(得分:1)

我发现了问题:

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

它是静态的,因此在实例化公共类并生成另一个ExecuteOnMainThread时会出现问题。

因此,只需删除“静态”并使其在每次由Unity创建ClassA时销毁并生成自己。

现在它就像一个魅力:)