Unity中C#协程数据库查询未按预期执行的问题求助
问题分析
你的问题核心是协程异步执行的特性——StartCoroutine启动后不会阻塞当前代码,所以getDatabaseValue里刚启动协程就立刻返回result,此时新的数据库查询还在网络请求阶段,拿到的自然是上次的旧数据。
解决方案
下面提供两种最常用的修正方式,适配不同的Unity开发习惯:
方法1:用回调函数处理异步结果(Unity传统写法)
既然是异步操作,就别指望同步返回结果,改成用回调在查询完成后拿到数据:
第一步:修改协程,添加回调参数
IEnumerator searchInDatabase(string sql, Action<string[,]> onCompleted) { queryCompleted = false; WWWForm database = new WWWForm(); database.AddField("sqlpost", sql); using (UnityWebRequest www = UnityWebRequest.Post("myDatabase", database)) { yield return www.SendWebRequest(); // 新增:网络请求错误判断 if (www.result != UnityWebRequest.Result.Success) { Debug.LogError($"请求失败:{www.error}"); onCompleted?.Invoke(null); yield break; } string[] entries = www.downloadHandler.text.Split(",|"); var result = new string[entries.Length, entries[0].Split(',').Length]; for (int i = 0; i < entries.Length; i++) { string[] parts = entries[i].Split(','); for (int j = 0; j < parts.Length; j++) { result[i, j] = parts[j]; } } queryCompleted = true; Debug.Log("Query Completed"); // 查询完成后调用回调返回结果 onCompleted?.Invoke(result); } }
第二步:修改调用逻辑,通过回调处理结果
void Update() { if (Input.GetKeyDown(KeyCode.A)) { GetDatabaseValueWithCallback("select * from table;", 0, 0, (value) => { Debug.Log("getting result data"); Debug.Log($"Result = {value}"); }); } } public void GetDatabaseValueWithCallback(string sql, int x, int y, Action<string> onValueGot) { Debug.Log("Waiting 00"); StartCoroutine(searchInDatabase(sql, (result) => { if (result != null && x < result.GetLength(0) && y < result.GetLength(1)) { onValueGot?.Invoke(result[x, y]); } else { Debug.LogWarning("索引无效或查询结果为空"); onValueGot?.Invoke(null); } })); }
执行顺序会完全符合你的预期:
Waiting 00 Query Completed getting result data Result = 2
方法2:用Async/Await(Unity 2017.2+支持)
如果你的Unity版本支持,可以用更简洁的异步语法,让代码看起来像同步逻辑:
第一步:把协程改成异步方法
async Task<string[,]> SearchInDatabaseAsync(string sql) { queryCompleted = false; WWWForm database = new WWWForm(); database.AddField("sqlpost", sql); using (UnityWebRequest www = UnityWebRequest.Post("myDatabase", database)) { await www.SendWebRequest(); if (www.result != UnityWebRequest.Result.Success) { Debug.LogError($"请求失败:{www.error}"); return null; } string[] entries = www.downloadHandler.text.Split(",|"); var result = new string[entries.Length, entries[0].Split(',').Length]; for (int i = 0; i < entries.Length; i++) { string[] parts = entries[i].Split(','); for (int j = 0; j < parts.Length; j++) { result[i, j] = parts[j]; } } queryCompleted = true; Debug.Log("Query Completed"); return result; } }
第二步:修改调用逻辑
void Update() { if (Input.GetKeyDown(KeyCode.A)) { GetDatabaseValueAsync("select * from table;", 0, 0); } } async void GetDatabaseValueAsync(string sql, int x, int y) { Debug.Log("Waiting 00"); var result = await SearchInDatabaseAsync(sql); Debug.Log("getting result data"); if (result != null && x < result.GetLength(0) && y < result.GetLength(1)) { Debug.Log($"Result = {result[x, y]}"); } else { Debug.LogWarning("索引无效或查询结果为空"); } }
额外注意事项
- 移除原来的全局
result变量,改成方法内部创建并返回,避免多查询时的变量覆盖问题。 - 必须添加网络请求错误判断,否则请求失败时会出现空引用异常。
- 两种方法都彻底解决了异步执行的顺序问题,不会再拿到旧数据。
内容的提问来源于stack exchange,提问作者Alex
相关产品推荐
相关产品推荐

