gpt4 book ai didi

wpf - 如何实现后台工作线程队列

转载 作者:行者123 更新时间:2023-12-03 10:20:59 25 4
gpt4 key购买 nike

我正在尝试使用 MVVM 模式实现我的第一个应用程序。我已经设法让大多数事情正常工作,但现在我面临以下(恕我直言很常见)场景的问题:

Button ( View )应调用方法(模型)。使用 ICommand (ViewModel) 这很容易。但是如果必须执行耗时的操作怎么办?

我当前的解决方案要求我实现 WorkQueue包含 WorkQueueItems 的类. WorkQueue有一个与之关联的线程,它执行 WorkQueueItem s。每个WorkQueueItem有一个 Name , 一个 StatusProgress在执行期间更新。
每个Window有自己的WorkQueue - 可视化为 StatusBar .

我的问题: ViewModel 如何找到合适的 WorkQueue ?我必须通过WorkQueue吗?对于我创建的每个 ViewModel(这真的很烦人)?或者还有其他我可以使用的机制吗?

我不是很熟悉RoutedCommand s——坚韧的基本概念似乎是朝着这个方向发展的。我希望看到一个可以绑定(bind) WorkQueueItem 的解决方案。到一个命令/事件,然后冒泡到包含 Window它被添加到 WindowWorkQueue .

我也考虑过制作WorkQueue一个单例 - 但这只适用于我只有一个 Window一次。

最佳答案

使用更高版本的 .Net Frameworks (4.0+) 和 WPF,您可以使用 System.Threading.Tasks 库提供了很多这样的工作。

如果说您的命令需要更新 View 模型上的属性,但它必须等待信息,您只需启动一个任务来执行 IO:

this.FindDataCommand = new RelayCommand<string>(
/* ICommand.Execute */
value =>
{
Task.Factory
.StartNew<IEnumerable<Foo>>(() => FindData(value))
.ContinueWith(
task =>
{
this.foundData.Clear();
this.foundData.AddRange(task.Result);
},
TaskScheduler.FromCurrentSynchronizationContext());
},

/* ICommand.CanExecute */
value => !String.IsNullOrWhitespace(value));

将其分解为可管理的部分,我们是 starting a new task它调用了一些方法 IEnumerable<Foo> FindData(string) .这是您一直编写的简单而无聊的同步代码。可能它已经存在于您的 View 模型中!

接下来我们告诉框架 start a new task when that one finishes使用 ContinueWith ,但要在 WPF Dispatcher instead 上进行.这使您可以避免 UI 元素的跨线程问题的麻烦。

您可以使用辅助类扩展它以进行监视:
public class TaskManager
{
private static ConcurrentDictionary<Dispatcher, TaskManager> _map
= new ConcurrentDictionary<Dispatcher, TaskManager>();

public ObservableCollection<WorkItem> Running
{
get;
private set;
}

public TaskManager()
{
this.Running = new ObservableCollection<WorkItem>();
}

public static TaskManager Get(Dispatcher dispatcher)
{
return _map.GetOrAdd(dispatcher, new TaskManager());
}
// ...

在 XAML 中使用此类将类似于将其实例添加到 Window 的 ViewModel 中。 :
public TaskManager CurrentTaskManager
{
get { return TaskManager.Get(Dispatcher.CurrentDispatcher); }
}
// <StatusBarItem Content="{Binding CurrentTaskManager.Running.Count}" />

然后,您将向 TaskManager 添加一个方法来处理向 Running 集合添加任务和从 Running 集合添加任务:
    public Task<TResult> StartNew<TResult>(Func<TResult> work)
{
var task = Task.Factory
.StartNew<TResult>(work);

// build our view model
var workItem = new WorkItem(task);
this.Running.Add(workItem);

// Pass the result back using ContinueWith
return task.ContinueWith(
t => { this.Running.Remove(workItem); return t.Result; },
TaskScheduler.FromCurrentSynchronizationContext());
}

现在我们只需更改我们的 FindDataCommand执行:
TaskManager.Get(Dispatcher.CurrentDispatcher)
.StartNew<IEnumerable<Foo>>(() => FindData(value))
.ContinueWith(
task =>
{
this.foundData.Clear();
this.foundData.AddRange(task.Result);
},
TaskScheduler.FromCurrentSynchronizationContext());
WorkItem类可能会暴露 Task 上的属性类添加到 UI,或者可以将其扩展为封装 CancellationToken以支持将来取消。

关于wpf - 如何实现后台工作线程队列,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/10180575/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com