2014-10-02 31 views
2

我有一個WPF(MVVM)項目,其中有多個視圖模型,每個模型都有一個按鈕,可以在同一個數據源上啓動不同的分析,在這種情況下是一個文件。該文件不能共享,因此如果在同一時間按下按鈕,則第二次調用將失敗。在WPF中排隊事件的最佳實踐

我需要一種排列按鈕點擊的方法,以便每個分析都可以按順序運行,但似乎無法使其運行。我嘗試使用靜態的Semaphore,SemaphoreSlimMutex,但它們似乎阻止了一切(Wait()函數似乎阻止當前正在運行的分析)。我試着用一個靜態對象lock()命令,但它似乎沒有阻止任何事件(我得到文件共享錯誤)。我也嘗試了一個線程池(最大併發線程數爲1),但它會更新UI的線程錯誤(這可能可以通過調用Invoke()來解決)。

我的問題是WPF在這種情況下可能被認爲是最佳實踐嗎?

編輯:我創建了展示我遇到的問題的樣機。它在http://1drv.ms/1s4oQ1T

+1

按鈕按鈕應該已經通過WPF Dispatcher類排隊。你是否以某種方式在工作線程上啓動分析? – 2014-10-02 16:56:24

+0

我沒有使用工作線程。我剛剛上傳了一個樣本。在真正的應用程序中,我們有一個使用Dispatcher調用來保持UI響應的進度條。 – 2014-10-06 20:39:22

+0

您想對點擊進行排隊嗎,還是排列在點擊上做點什麼的任務? – 2014-10-06 21:05:20

回答

1

我會做兩件事情來解決這個問題:

首先,在命令模式封裝分析操作。如果您不熟悉它,最簡單的實現是具有單一功能的接口Execute。當您想要執行分析操作時,只需創建其中一個。您也可以使用內置的ICommand接口來提供幫助,但請注意,該接口比通用命令模式更重要。

當然,創造只是戰鬥的一半,所以在這樣做後,我會將它添加到BlockingCollection。這個集合是.NET對生產者 - 消費者問題的解決方案。使用集合的GetConsumingEnumerable方法中的foreach具有使用此集合的後臺線程(執行包含在其中的命令對象),並且您的按鈕將「饋送」它。

foreach (var item in bc.GetConsumingEnumerable()) 
{ 
    item.Execute(); 
} 

MSDN阻斷收藏:http://msdn.microsoft.com/en-us/library/dd267312(v=vs.110).aspx

現在,所有的信號量,等待等等都做了你,你可以添加一個操作隊列(如果它需要一個隊列,請考慮使用ConcurrentQueue作爲BlockingCollection的後備集合),並在UI線程上返回。後臺線程將選擇任務並運行它。

需要Invoke任何UI更新從後臺線程當然,沒有得到解決這個問題:)。

+0

我上傳了一個示例項目。在它和真正的應用程序中,事件通過MVVM連接到命令。你有BlockingCollection的樣品嗎? – 2014-10-06 20:40:22

+0

@WalterWilliams在我的設計中,MVVM命令可以簡單地將不同的命令對象添加到隊列中進行處理。 – BradleyDotNET 2014-10-06 21:34:51

+0

其實,它聽起來像他只是需要一個異步的ICommand隊列... – 2014-10-06 21:47:20

0

我建議在視圖模型共享的調度對象中使用消費者任務的隊列,該消費者任務在隊列上等待添加項目。當按下按鈕時,視圖模型會將工作項添加到隊列中。消費者任務每次從隊列中取一個項目,分析包含在工作項目中,然後在隊列中檢查另一個項目,如果沒有要處理的工作項目,則等待更多工作項目添加。

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綁定工作等。