C# 在另一个线程(Unity3D)中从数据库检索数据

C# 在另一个线程(Unity3D)中从数据库检索数据,c#,multithreading,unity3d,C#,Multithreading,Unity3d,我目前有一个从数据库检索数据并在unity3D中可视化的代码。但是,每次在FixedUpdate()函数中检索数据时,它都会每隔1秒急剧增加一次。我正在考虑使用线程来实现这一点,但我不确定我做错了什么 这是我在线程中调用的函数 public void retrievefromDB(){ if (timeStep - prevTimeStep > 99) { ti

我目前有一个从数据库检索数据并在unity3D中可视化的代码。但是,每次在FixedUpdate()函数中检索数据时,它都会每隔1秒急剧增加一次。我正在考虑使用线程来实现这一点,但我不确定我做错了什么

这是我在线程中调用的函数

 public void retrievefromDB(){
                            if (timeStep - prevTimeStep > 99) {
                                    timeStep -= 1; //special for this dataset
   query = "SELECT * FROM GridData2 WHERE timestep=" + timeStep;

                                    if (showParent)
                                            query += " AND (Level != 10)";
                                    else
                                            query += " AND (Level == 10)";

      query += " AND temperature >= " + minTemp + " AND temperature <= " + maxTemp;
                                    dt.Rows.Clear ();
                                    dt = sqlDB.ExecuteQuery (query);

                                    prevTimeStep = timeStep;
                            }

            }
在把它放进一个线程后,它在一段时间后不断地破坏场景。
谁能告诉我我做错了什么?我该怎么解决呢?

无论你做什么,你都需要停止每帧访问数据库

您只需要每隔60帧或更多帧生成一次结果。您可以通过使用一个变量轻松地完成此操作,在该变量中,您可以将自上次调用以来经过的时间相加

对于Unity中的多线程,您有三种选择:

  • 用于统一的多线程框架,如

  • C#内置线程
    您需要小心不要从生成的辅助线程调用特定于Unity的API。发送Vector3、Color等数据结构是可以的,但不要调用GameObjects或Components等参考对象。

  • 统一合作计划
    协程是Unity模拟多线程的方式。它是一个非常强大的工具,可以让事情在同一线程中异步运行


最初问题的原因相对明显:数据库访问速度慢。如果在
FixedUpdate
方法中内联调用数据库,则在数据库访问发生时,您将暂停游戏的移动(例如,如果必须初始化连接,这可能需要一秒钟)

发布的线程代码的主要问题是每次调用
FixedUpdate
时都会启动一个新线程。这意味着你将每秒启动60个新线程(默认情况下),这将很快削弱你的游戏

虽然在这种工作中可以统一使用C#threads,但更好的方法是创建单个线程并允许其管理计时,而不是在每次作业运行时创建一个新线程。这意味着在
Awake()
Start()
中创建线程,然后使用
thread.Sleep
或类似方法来处理计时

协同程序(正如Mihai在回答中所建议的那样)对于确定事件的时间非常有用,但它们仍然在游戏线程上运行:如果您将DB代码放在协同程序中,则在运行时仍然会看到暂停。如果必须每秒运行此DB访问,则需要将其放在适当的线程中


也就是说,您是否认为数据库访问可能是不必要的?一个更高性能的模型可能是预先缓存所有数据,并在需要时从内存中使用这些数据。(如果数据是非常动态的,或者如果您在内存受限的环境(如移动设备)中运行,这可能是不可能的…

现在终于可以工作了。只需在retrievefromDB()中添加这些

我正在使用来自的threadhelper 所以你可以去看看。
感谢所有帮助过我的人!:)

哦,还有一个问题。如果我想让函数保持每秒更新,我需要更改什么?你可以每秒调用它<代码>任务。工厂使用一个线程池,因此它不像看上去那样昂贵。然而,出于这个目的,协同程序更适合。你还需要停止每帧调用这个。您只需要每隔60帧或更多帧生成一次结果。您可以通过使用一个变量轻松地完成此操作,在该变量中,您可以将自上次调用以来经过的时间相加。通常,如果没有某种同步,您不能访问共享资源。即使假设
sqlDB
是线程安全的(不一定是这样),
dt.Rows.Clear()
dt=…
也绝对不是线程安全的。这只是一场即将发生的灾难。如果你想坚持使用这种代码(这不是一个好主意),你至少需要
锁定
dt
的所有访问。当然,在您的代码原样中,这将再次消除并行性,但只需确保只更改
lock
中的
dt
——在获取锁之前加载数据。同样的情况也适用于
timeStep
等。“在将其放入线程后,它会在一段时间后继续破坏场景”-将为每个
FixedUpdate()
创建一个新线程,从而导致崩溃。Luaan你能给我一个“锁定”代码的示例吗?我不知道你在说什么,但这似乎很重要,如果我在Start函数中调用线程,它不会更新,只会运行一次。有没有办法不停地重复使用线程而不是创建一个新的线程?在线程的方法中加入一个循环,这样它就可以无限期地运行(具有所需的延迟)。您可能还需要一个标志,可以用来通知线程在请求时干净地终止?那么我该如何添加延迟呢?是的,我的意思是-或者更可能的是,
while(!\u threadShouldEnd)
,这样您就可以从线程外部控制终止。如果在专用线程中运行,那么使用
thread.Sleep
添加延迟是没有问题的。
void FixedUpdate()
    {
    Thread testthread = new Thread(new ThreadStart(retrievefromDB));
        testthread.Start ();
}
using System.Threading.Tasks;  

public class Example
{
    void StartOnDifferentThread()
    {
        Task.Factory
            .StartNew(() =>
            {
                FunctionToRun();
            })
            .ContinueWith(task =>
            {
                if (task.IsCompleted)
                {
                    // handle result
                }
                else if (task.IsFaulted)
                {
                    // handle error
                }
            });
    }

    void FunctionToRun()
    {
        // do stuff
    }
}
public void retrievefromDB(){
      while(true){
                            if (timeStep - prevTimeStep > 99) {
                                    timeStep -= 1; //special for this dataset
   query = "SELECT * FROM GridData2 WHERE timestep=" + timeStep;

                                    if (showParent)
                                            query += " AND (Level != 10)";
                                    else
                                            query += " AND (Level == 10)";

      query += " AND temperature >= " + minTemp + " AND temperature <= " + maxTemp;
                                    dt.Rows.Clear ();
                                    dt = sqlDB.ExecuteQuery (query);

                                    prevTimeStep = timeStep;
                            }
           Thread.Sleep(1);
               }
            }
testThread = UnityThreadHelper.CreateThread (() =>
                                                     {
            UnityThreadHelper.TaskDistributor.Dispatch (() => retrievefromDB ());

        });

        testThread.Start ();