我有一个WPF(MVVM)项目,我有多个视图模型,每个视图模型都有一个按钮,可以在同一个数据源上启动不同的分析,在这种情况下是一个文件。该文件无法共享,因此如果在同一时间按下按钮,则第二次调用将失败。
我需要一种方法来对按钮进行排队,这样每个分析都可以按顺序运行,但我似乎无法让它工作。我尝试使用静态Semaphore
,SemaphoreSlim
和Mutex
,但它们似乎会停止所有内容(Wait()
函数似乎会阻止当前运行的分析)。我尝试了一个带有静态对象的lock()
命令,但它似乎没有阻止任何一个事件(我得到文件共享错误)。我还尝试了一个线程池(最大并发线程数为1),但它会更新UI时出现线程错误(这可能通过Invoke()
调用解决)。
我的问题是WPF在这种情况下可能被认为是最佳做法吗?
编辑:我创建了一个展示我遇到的问题的模型。它位于http://1drv.ms/1s4oQ1T。答案 0 :(得分:1)
我会做两件事来解决这个问题:
首先,将分析操作封装在命令模式中。如果您不熟悉它,最简单的实现是具有单个函数Execute
的接口。如果要执行分析操作,只需创建其中一个。您也可以使用内置的ICommand
接口来提供帮助,但请注意,此接口比通用命令模式具有更多功能。
当然,创造只是战斗的一半,所以我这样做后会把它添加到BlockingCollection
。这个集合是.NET对Producer-Consumer问题的解决方案。有一个后台线程使用集合foreach
方法上的GetConsumingEnumerable
来使用此集合(执行其中包含的命令对象),并且您的按钮将" feed"它。
foreach (var item in bc.GetConsumingEnumerable())
{
item.Execute();
}
阻止收集的MSDN:http://msdn.microsoft.com/en-us/library/dd267312(v=vs.110).aspx
现在,所有的信号量,等待等都是为你完成的,你只需要向队列添加一个操作(如果它需要是一个队列,考虑使用ConcurrentQueue
作为{的后备集合{1}})并返回UI线程。后台线程将选择任务并运行它。
当然需要BlockingCollection
来自后台线程的任何UI更新,而不是解决该问题:)。
答案 1 :(得分:0)
我建议在视图模型共享的调度对象中使用一个队列,该队列等待队列添加一个项目。按下按钮时,视图模型会将工作项添加到队列中。使用者任务每次从队列中获取一个项目,分析包含在工作项中,然后检查队列中的另一个项目,等待更多工作项目添加,如果没有要处理的工作项目。
答案 2 :(得分:0)
这里需要的是一个异步队列,这样您就可以排队这些任务而不会阻塞您的线程。 SemaphoreSlim
实际上有WaitAsync
方法,这使得创建这样的队列变得相当简单:
public class TaskQueue
{
private SemaphoreSlim semaphore;
public TaskQueue()
{
semaphore = new SemaphoreSlim(1);
}
public async Task<T> Enqueue<T>(Func<Task<T>> taskGenerator)
{
await semaphore.WaitAsync();
try
{
return await taskGenerator();
}
finally
{
semaphore.Release();
}
}
public async Task Enqueue(Func<Task> taskGenerator)
{
await semaphore.WaitAsync();
try
{
await taskGenerator();
}
finally
{
semaphore.Release();
}
}
}
这允许您将所有顺序执行的操作排队,而不是并行执行,并且不会在任何时候阻塞任何线程。这些操作也可以是任何类型的异步操作,无论是另一个线程中的CPU绑定工作,IO绑定工作等等。