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

发布于 2024-11-10 16:05:00 字数 342 浏览 7 评论 0原文

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

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

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

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

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

谢谢

I am building a WPF which has a button that execute a sql query in sql server (the query could take a long time to run).
I want to use TPL for doing that.

This code:
var result = Task.Factory.StartNew(() => { command.ExecuteNonQuery(); });

gives this exception:
ExecuteNonQuery requires an open and available Connection. The connection's current state is closed.

I guess this is due to the fact that the query runs on a different thread and is not aware of the open connection.

I have 2 questions:
1. How do I make the new thread know of this open connection?
2. After solving this ,How do I get the window not to freeze due to this query.

Thanks

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

揽月 2024-11-17 16:05:00

您必须在任务主体中创建并打开此命令的连接。要么不关闭任务外部的连接,我认为这就是您在这里所做的,但无法从您粘贴的一行代码中看出。

我个人会在任务主体内完成这一切。如果用户不需要的话,为什么他们必须等待您甚至获得连接/命令设置?此外,您的连接也有可能是共享实例,并且无法跨线程工作。

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

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

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

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

You will have to create and open the connection for this command within the Task's body. Either that or don't close the connection outside the Task, which I assume is what you're doing here, but can't tell from the one line of code you pasted.

I would personally do it all inside the Task body. Why should the user have to wait for you to even get the connection/command setup if they don't have to? Also there's the chance that you connection is a shared instance and that won't work across threads.

Once you get the DB work into a Task it will be executed on a Thread Pool thread by default which will free up the WPF dispatcher thread to go back to processing UI events preventing the "freezing". Most likely you will want to update the UI after that DB task has completed and to do that you would hpjust add a continuation task, but in order to be able to manipulate the UI from that continuation task you need to make sure it's explicitly scheduled to run on the Dispatcher thread. This is done by explicitly specifying a TaskScheduler for the current synchronization context while scheduling the continuation. That would look something like this:

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

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

The magic here is the use of the TaskScheduler::FromCurrentSynchronizationContext method which will schedule the continuation to be executed on the Dispatcher thread of the current call.

旧时光的容颜 2024-11-17 16:05:00

除了@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());

In addition to @Drew Marsh answer,

To avoid Exception:

The current SynchronizationContext may not be used as a TaskScheduler

You can use check for Synchronization Content Exists:

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

And use it instead:

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

backgroundDBTask.ContinueWith((t) =>
{
    //... UI update work here ...
},
GetSyncronizationContent());
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文