为什么回调中的这个会导致代码停止?

why this in a callback causes the code to stop?

在使用 GetValueAsync().ContinueWith(task..) 中的回调通过 Firebase 恢复我的数据后,我想实例化我的预制件以查看排行榜的分数列表。但是,它什么也没做,我也没有错误。一旦遇到 'this' 或 'instantiate'.

,代码就会在回调 UseSores 中停止
public class Leaderboardmanager : MonoBehaviour
{

    public GameObject rowLeardBoard;
    FirebaseDB_Read read;
    float positionX; 
    int nbRows = 10;

    void Start()
    {   
        read = (gameObject.AddComponent<FirebaseDB_Read>());
        GetScorePlayer();
    } 

    void GetScorePlayer()
    {
        read.GetScores(UseScores, "entries/LeaderBoard/", nbRows);
    }

    void UseScores(IList<FirebaseDB_Read.Score> scores)
    {
        Debug.Log("arrive here");
        positionX = this.transform.position.y; 
        Debug.Log("does not arrive here");
    }
}

这是获取我的数据:

public class FirebaseDB_Read : MonoBehaviour
{

    public class Score
    {
        public string UID;
        public string score;
        public int rank;
    }


    public void GetScores(Action<IList<Score>> callback, string URL_TO_SCORES, int limit)
    {
        DatabaseReference scoresRef = FirebaseDatabase.DefaultInstance.GetReference(URL_TO_SCORES);

        scoresRef.OrderByChild("score").LimitToLast(limit).GetValueAsync().ContinueWith(task =>
        {
            DataSnapshot snapshot = task.Result;
            IList<Score> objectsList = new List<Score> { };

            int i = 1;
            foreach (var childSnapshot in snapshot.Children)
            {
                Score score = new Score();
                score.rank = i;
                score.UID = childSnapshot.Child("UID").GetValue(true).ToString();
                score.score = childSnapshot.Child("score").GetValue(true).ToString();

                objectsList.Add(score);
                i++;
            }

            callback(objectsList);
        });
    }
}

这是 Unity 中的一个常见问题:因为您 ContinueWith 后台线程上 !

Unity 不是 thread-safe,这意味着大多数 Unity API 只能在 Unity 主线程中使用。

Firebase 专门为 Unity 提供了一个扩展:ContinueWithOnMainThread 确保结果在访问 API 有效的 Unity 主线程中处理。

scoresRef.OrderByChild("score").LimitToLast(limit).GetValueAsync().ContinueWithOnMainThread(task =>
{
    ...
});

作为替代方案,您可以使用一种所谓的“主线程调度程序”模式,并确保 callback 在接收方的主线程中执行。这样做的好处是列表中仍然昂贵的操作都在后台线程上执行,不会影响 UI 性能

scoresRef.OrderByChild("score").LimitToLast(limit).GetValueAsync().ContinueWith(task =>
{
    ...
});

但随后在 FirebaseDB_Read

的接收端
private readonly ConcurrentQueue<Action> _mainThreadActions = new ConcurrentQueue<Action>();

private void Update()
{
    if(_mainThreadAction.Count > 0)
    {
        while(_mainThreadActions.TryDequeue(out var action))
        {
            action?.Invoke();
        }
    }
}

void GetScorePlayer()
{
    read.GetScores(UseScores, "entries/LeaderBoard/", nbRows);
}

void UseScores(IList<FirebaseDB_Read.Score> scores)
{
    // handle this in the next main thread update
    _mainThreadActions.Enqueue(() =>
    {
        Debug.Log("arrive here");
        positionX = this.transform.position.y; 
        Debug.Log("does not arrive here");
    }
}

当然,越位会引入一些开销来检查 Update 中的任何新操作。因此,如果您打算使用多个此类后台操作,请确保在一个中心位置实施它们,以限制开销;)