首页 文章

使用TPL时避免窗口(WPF)冻结

提问于
浏览
2

我正在构建一个WPF,它有一个在sql server中执行sql查询的按钮(查询可能需要很长时间才能运行) . 我想使用TPL来做到这一点 .

此代码:var result = Task.Factory.StartNew(()=> {command.ExecuteNonQuery();});

给出了这个异常:ExecuteNonQuery需要一个开放且可用的连接 . 连接的当前状态已关闭 .

我想这是因为查询在不同的线程上运行而且不知道打开的连接 .

我有两个问题:1 . 如何让新线程知道这个开放连接? 2.解决此问题后,如何通过此查询使窗口不冻结 .

谢谢

2 回答

  • 6

    您必须在Task的正文中创建并打开此命令的连接 . 要么关闭或不关闭任务外部的连接,我认为这是你在这里做的,但无法从你粘贴的一行代码中分辨出来 .

    我个人会在Task团体中亲自完成这一切 . 为什么用户必须等待你甚至不必进行连接/命令设置?此外,您连接的可能性是共享实例,并且无法跨线程工作 .

    一旦你将数据库工作变成一个任务,它将默认在线程池线程上执行,这将释放WPF调度程序线程以返回处理UI事件,防止“冻结” . 最有可能的是,您需要在完成数据库任务后更新UI,为此您需要添加延续任务,但为了能够从该延续任务中操作UI,您需要确保将其明确安排到在Dispatcher线程上运行 . 这是通过在调度延续时显式指定当前同步上下文的TaskScheduler来完成的 . 这看起来像这样:

    Task backgroundDBTask = Task.Factory.StartNew(() =>
    {
        ... DB work here ...
    });
    
    backgroundDBTask.ContinueWith((t) =>
    {
        ... UI update work here ...
    },
    TaskScheduler.FromCurrentSynchronizationContext());
    

    这里的神奇之处在于使用 TaskScheduler::FromCurrentSynchronizationContext 方法,该方法将调度继续在当前调用的Dispatcher线程上执行 .

  • 1

    除了 @Drew Marsh 答案,

    To avoid Exception:

    当前的SynchronizationContext可能不会用作TaskScheduler

    You can use check for Synchronization Content Exists:

    private static TaskScheduler GetSyncronizationContent() => 
         SynchronizationContext.Current != null ? 
              TaskScheduler.FromCurrentSynchronizationContext() : 
              TaskScheduler.Current;
    

    而是使用它:

    Task backgroundDBTask = Task.Factory.StartNew(() =>
    {
        //... DB work here ...
    });
    
    backgroundDBTask.ContinueWith((t) =>
    {
        //... UI update work here ...
    },
    GetSyncronizationContent());
    

相关问题