2011-05-31 75 views
2

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

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

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

我想这是由于查询在不同的线程上运行并且不知道打开的连接。

我有2个问题: 1.如何让新线程知道这个打开的连接? 2.解决此问题后,如何让窗口不因该查询而冻结。

感谢

回答

6

你必须创建和打开任务的身体内这个命令的连接。或者不要关闭任务外的连接,我认为这是你在这里做的,但是不能从你粘贴的一行代码中分辨出来。

我个人会在任务内部完成所有工作。为什么用户不得不等待你连接/命令设置,如果他们不需要?此外,有可能你的连接是一个共享实例,并且不会跨线程工作。

一旦你将数据库工作纳入任务,它将默认在线程池线程上执行,这将释放WPF调度程序线程以回到处理UI事件以防止“冻结”。在完成数据库任务后,很可能需要更新用户界面,并且为了能够从该延续任务操作界面,您需要确保将其显式调度为在分派器线程上运行。这是通过在调度延续时显式指定当前同步上下文的TaskScheduler来完成的。这将是这个样子:

Task backgroundDBTask = Task.Factory.StartNew(() => 
{ 
    ... DB work here ... 
}); 

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

这里的魔法是利用TaskScheduler::FromCurrentSynchronizationContext方法,将安排要在当前通话的调度线程上执行的延续。

+0

感谢,何谈第二个问题? – 2011-06-02 06:52:32

+0

增加了更多的细节来回答。 – 2011-06-02 14:32:43

1

除了@Drew沼泽答案,

为了避免异常:

目前的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());