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

时间:2011-05-31 14:04:18

标签: wpf window freeze task-parallel-library ui-thread

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

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

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

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

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

由于

2 个答案:

答案 0 :(得分: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 :(得分:1)

除了 @Drew Marsh 回答,

避免例外:

  

当前的SynchronizationContext不能用作TaskScheduler

您可以使用检查同步内容存在:

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());