2010-05-20 28 views
0

嗨,我有一個生產者 - 消費者模式。 WPF UI對於長時間運行的工作線程來說是一種輔助工具,它正在偵聽數據包並使任務進入隊列。在實踐中,所有消息都被出隊,然後UI消費者進行處理。WPF工作者線程需要通過消費者通知暫停

我遇到的問題是我有一個UIcontroller類負責所有的WPF GUI組件。它打開一個新窗口,顯示每個出隊任務並保存一個線程安全的引用窗口集合。

我通過Despatcher.BeginInvoke發出信號。那裏沒有驚喜。

我遇到的問題是,如果UIController類成功打開一個配置數量的窗口,我想暫停/逐字停止我的工作線程。打開的窗口數量有限制。一旦打開的窗口數量減少回到範圍內,我將恢復對隊列的處理。我試着把這個窗口集合保存在這個類中,並通過調用將它傳遞給UI,在這裏通過引用更新它,但Begin Invoke是異步的,並且計數在工作線程的循環中沒有及時更新。

我可以在UIcontroller類中測試打開的窗口的否,忽略甚至重新從那裏執行任務 - 基本上不採取任何行動。但我想要一個更清潔的解決方案。

我可以做一些乾淨的回調嗎?

public void Work() 
    { 

     while (true) 
     { 

      Log.Instance.Info("****In worker THREAD " + _worker.Name); 

      while (_controller.IncomingMessages.Count > 0 && [--test for no of windows open on GU thread somehow - I did try holding a reference to the collection in this class--]) 
      { 
       try 
       { 
        Log.Instance.Info("****In Notification worker THREAD and messages to process " + _worker.Name); 

        Messages.AlertMessage task = null; 
        lock (_locker) 
        { 

         if (_controller.IncomingMessages.Count > 0 && ToasterPopUps.Count < 5) 
         { 
          task = _controller.IncomingMessages.Dequeue(); 
          if (task == null) 
          { 
           return; 
          } 
         } 

         if (task != null) 
         { 
          Log.Instance.Info("Dequeing: " + task + " " + task.ID + " from Notification thread"); 

       _UIthread.BeginInvoke(DispatcherPriority.Background, new JoinUIThread(DespatchUIThread), task, _UIthread); 
         } 
        } 

       } 
       catch (Exception err) 
       { 
        Log.Instance.Critical(string.Format("Unexpected Error in PollPopUp Thread Queue {0} ", err)); 
       } 

      } 

      Log.Instance.Info("No more Notification tasks - wait for a signal"); 

      _wh.WaitOne(); 

     } 

    } 

    public void DespatchUIThread(Messages.AlertMessage task, System.Windows.Threading.Dispatcher dispatcherThread) 
    { 
     try 
     { 
      _controller.CreateWindow(task, dispatcherThread); 
     } 
     catch (Exception err) 
     { 
      Log.Instance.Critical("Critical error " + err); 
     } 
    } 

回答

0

如果我理解你,你希望生產者線程等待,而UI窗口的數量是在一定的閾值。我會使用生產者和使用者共享的信號量,並初始化爲最大窗口數來完成此行爲。

const int MaxWindowCount = 5; 
Sempahore semaphore = new Semaphore(0, MaxWindowCount); 

每個入隊之前,你叫

semaphore.WaitOne(); 

一旦每個任務完成,它的窗口關閉,呼叫

semaphore.Release(); 

一旦MaxWindowCount項目已排隊,你的生產者將等待其下一個電話WaitOne()並將等到發佈()被調用。

嗯,這可能不會達到你想要的,因爲它限制隊列中MaxWindowCount的數量,不一定是打開窗口的數量。它假設排隊中的每個項目都有一個打開的窗口,在您的情況下可能不是這樣。

如果這是一個無效的假設,您可能有一個線程負責從隊列中拉出項目。這個線程將負責調用WaitOne(),同時將項目交給UIController。 UIController仍然負責呼叫版本()。實際上,你會有兩個生產者 - 消費者隊列。一個用於任務的無限隊列和一個用於Windows的有界隊列。

--Added 2010年5月21日 -

另一種選擇可能是有通話與CreateWindow採取一個回調參數,告訴控制器打開的窗口數量。

使用以下委託作爲CreateWindow的參數,可以保持Worker中的打開窗口的計數,並在達到最大值時相應地執行相應操作。

public delegate void CreateWindowCallback(int numOpenWindows); 

你可能會保持你的工作人員打開窗戶的計數,你需要讓工作人員知道最大窗口數。或者,您可以在Controller類中保留CanOpenNewWindows布爾值。

呼叫前_UIthread.BeginInvoke工人可以檢查當前窗口計數(或CanOpenNewWindows財產),如果它不應該打開一個新的窗口,調用了WaitOne()。您仍然需要知道什麼時候關閉窗口,以釋放/通知工作人員繼續。

在窗口創建失敗或控制器不需要顯示窗口的情況下,您可以讓工作人員知道打開的窗口計數沒有增加。這確實假定_controller知道是否創建了一個窗口。

- 另一種思考 -

它可能會保留所有的窗口創建邏輯在你的UIController。 UIController需要知道UIThread並將CreateWindow調用修改爲只接受任務。

的調用CreateWindow(任務)將調用BeginInvoke,如果有少於最大窗口打開。否則,它會調用WaitOne()並等待其中一個窗口發出信號表明它們已關閉。我們可以在這裏使用信號量,在每次成功創建窗口後調用WaitOne()。每個窗口在關閉時都會負責調用Release()。

這樣,當工作人員直接調用CreateWindow時,它會在達到最大窗口計數時阻塞,並且永遠不需要知道最大窗口數或關於UI的其他更多信息。

+0

感謝您的意見。問題在於消費者可能無法打開窗戶,或者決定不打開窗戶。 最後的建議聽起來不錯。我不清楚這究竟會如何工作,但我會玩一玩。 – Mike 2010-05-20 21:58:56

+0

雖然我研究了有界和無界的解決方案,但我只是在隊列中將(不希望的)消息重新排入隊列,取得了一些成功。這是一個混亂,因爲我不得不在構造函數中傳遞相當數量的類,以便UI控制器可以看到我的隊列。我做了什麼,我需要重構。 – Mike 2010-05-20 22:40:40

+0

我的(令人驚訝的直截了當,正如它發生的那樣)解決方案是通過調用UI控制器來(樂觀地)打開最多的窗口。然後我等一下。 UI控制器重新激活工作線程,並將計數重置爲可用窗口的數量:每當關閉一個或多個窗口時... 唯一的問題是,如果一個或多個窗口無法打開,我只能打開1窗口而不是允許的最大值。如果一切都打不開,我可能會永久封鎖。我可以在錯誤處理中處理這個問題。 – Mike 2010-05-21 02:31:27

相關問題