WPF、MVVM 和异步工作

发布于 2024-11-29 15:32:51 字数 1202 浏览 4 评论 0原文

我知道这个问题以前曾在这里被问过,但我读过几个答案对我没有帮助。

我有一个组合框,需要从数据库中获取与选择相关的一些信息(可能是在失去焦点时,在滚动期间停止一千个调用)。此信息仅供显示,并不重要,因此进入后台线程/任务听起来是完美的解决方案。不过,它确实需要几秒钟的时间,因为它正在从一些非常大的表中获取计数。用户应该可以自由地继续执行其他任务,因为此信息实际上仅供显示/参考。

此问题提倡使用后台工作线程,但此解决方案有两个问题。 1) 在工作线程已经运行时更改选择会带来问题。您可以不第二次启动它,这意味着当它返回时,它不再显示新选择的有效信息,或者尝试取消它(这并不总是有效)。 2)出于某种我无法解释的原因,如果该方法位于 Model 中,则实际为后台工作人员访问数据库的方法返回的速度会比在 ViewModel 中慢,而我认为它不属于 ViewModel。我真的不知道为什么。

此问题有几票,但OP 的问题措辞非常糟糕,所选答案只是说“是的,应该有效”。

这个问题的方法看起来很有希望,但是链接视频长达一个小时(我看完了整个过程),只涉及调度员 10-15 秒,没有解释。如果有人有一篇文章的链接,该文章更深入地介绍了这种方法,那就太好了。

线程池,如此处建议的所示这可能是最好的方法,因为多个查找请求只是排队,而不是导致已经运行的错误。但是,它没有解释如何使用线程池,而是链接到 MSDN 文章。如果有人有一篇文章的链接,该文章更深入地介绍了这种方法,那将是理想的,因为它似乎是更好的解决方案(当然,我可能是错的)。

我确实尝试过对此进行研究,但大多数答案只是告诉您使用什么方法,而不是如何使用它。我真的在寻找“如何做”。

I know this question has been asked here before, but I've read through several answers that haven't helped me.

I have a ComboBox that needs to get some information related to a selection from a database (probably on lost focus, to stop a thousand calls during scrolling). This information is for display only, and not critical, so getting on a background thread/task sounds like the perfect solution. It does take several seconds though, as it is getting counts from some very large tables. The user should be free to move on to other tasks, as this information really is just for display/reference only.

This question advocates using a Background worker, but this solution has two issues. 1) Changing the selection while the worker is already running introduces problems. You can either not start it the second time, meaning when it returns it is no longer showing valid info for the new selection, or try to cancel it (which doesn't always work). 2) For some reason I cannot explain, the method that actually accesses the database for the background worker returns slower if the method is in Model than if in the ViewModel, where I do not think it belongs. I really don't know why.

This question has several votes, but the OP's question is worded very poorly, and the selected answer just says "yeah that should work."

This question's method looks promising, but the linked video is an hour long (I watched the whole thing), and touches on the dispatcher only for 10-15 seconds without explaining it. If someone has a link to an article that covers this method more in-depth, that would be good.

Thread pooling, as suggested here looks like it is probably the best way to go, as multiple requests for lookup just get queued, instead of causing already-running errors. However, it has no explanation of how to use a thread pool, instead linking to the MSDN article. If someone has a link to an article that covers this method more in-depth, that would be ideal, as it seems like the better solution (of course, I could be wrong).

I really tried to do my research on this one, but most of these answers just tell you what method to use, not how to use it. I am really looking for a "how-to."

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

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

发布评论

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

评论(2

π浅易 2024-12-06 15:32:51

好的。您的问题:

  1. 您有一个带有项目列表的选择控件
  2. 您有一个昂贵操作,该操作从当前选定的项目返回一些结果(请注意,此操作应该是昂贵的,而不是只是需要时间返回,以免你担心同时返回的结果太多)——所以你需要并行执行
  3. 返回的结果不会被执行,只显示——所以异步执行
  4. 如果当前选定的项目发生更改,您将不再需要之前的结果 - 并且应尽快取消以前的请求,因为它们昂贵

您应该使用最新的 .NET 技术做什么:

  1. 使用响应式扩展 (Rx),设置限制,以便仅在以下情况下触发用户将当前选择保留至少 500 毫秒(当用户按住向下箭头键时,您不希望生成许多任务)
  2. 当油门启动时,调用 async 方法(异步 CTP) 等待任务中的操作(长时间运行以避免线程池饥饿),并放入取消令牌;保存当前选择以供稍后比较
  3. 当操作返回时,将结果设置到您的数据上下文中(您的显示控件应绑定到该数据上下文)——异步方法始终在 UI 线程上继续,因此您不必担心线程访问
  4. 如果节流阀触发并且存在未完成的任务/取消令牌,请首先使用取消令牌取消任务,然后再按照 #2 生成新任务。由于任务被取消,await 会抛出异常,但这并不重要,因为您不再需要它了。
  5. 这里不存在并发问题,因为异步 CTP 始终在 UI 线程上继续。就您的所有操作而言,它们都是单线程的,不会相互干扰。

我认为如果你使用 Async CTP 和 Rx,大约需要 10 行代码。

注意:如果您的操作不昂贵,则不必使用取消令牌。仅允许任务运行完成,但忽略结果。不过,仍然建议您尽早取消数据库查询,虽然在客户端机器上并不昂贵,但在服务器上却很昂贵。

OK. Your question:

  1. You have a selection control with list of items
  2. You have an expensive operation that returns some result from the currently selected item (Note that this operation should be expensive, not just takes time to return, in order to have you worry about not having too many of them at the same time) -- so you need to do it in parallel
  3. The returned result is not acted upon, only displayed -- so do it asynchronously
  4. If the currently selected item changes, you no longer want the previous result -- and the previous requests should be cancelled as soon as possible because they are expensive

What you should do, with the latest .NET technologies:

  1. Use Reactive Extensions (Rx), set up a throttle so that it only fires when the user keeps the current selection for, say, at least 500ms (you don't want to spawn many many taks when the user keeps pressing the down arrow key)
  2. When the throttle fires, call an async method (Async CTP) which await the operation in a Task (long-running to avoid starving the thread pool), and also put in a Cancellation token; save the current selection for comparison later
  3. When the operation returns, set the result into your data context (which your display control should be bound to) -- the async method always continues on the UI thread, so you don't have to worry about thread access
  4. If the throttle fires and there is an outstanding task / cancellation token, first use the cancellation token to cancel the task, before spawning a new task as per #2. The await will throw because the Task is cancelled, but it doesn't matter since you don't need it any more.
  5. There is no concurrency issues here because the Async CTP always continues on the UI thread. As far as all your operations are concerned, they are all single-threaded and won't tread on each other.

I think if you use the Async CTP with Rx, it is about 10 lines of code.

Note: If your operation is NOT EXPENSIVE, you don't have to use a cancellation token. Just allow the task to run to completion, but ignore the result. However, it is still recommended that you cancel a database query early, although it is not expensive on the client machine, it is expensive on the server.

走过海棠暮 2024-12-06 15:32:51

您可以尝试使用异步绑定:

<ComboBox Name="theCombo" ... />
<TextBlock Text="{Binding Path=SomeSlowProperty, ElementName=theCombo, IsAsync=True}" />

You could try to use an async binding:

<ComboBox Name="theCombo" ... />
<TextBlock Text="{Binding Path=SomeSlowProperty, ElementName=theCombo, IsAsync=True}" />
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文