我正在构建一个WPF,它有一个在sql server中执行sql查询的按钮(查询可能需要很长时间才能运行)。 我想用TPL来做这件事。
此代码: var result = Task.Factory.StartNew(()=> {command.ExecuteNonQuery();});
给出了这个例外: ExecuteNonQuery需要一个开放且可用的连接。连接的当前状态已关闭。
我想这是因为查询在不同的线程上运行而且不知道打开的连接。
我有两个问题: 1.如何让新线程知道这个开放连接? 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());