2014-11-21 54 views
3

我已經繼承了一箇舊的VB6 DLL來控制工業機械。我已將它轉換爲C#。除了一個特性之外,它運行良好:VB6版本通過一個定時器支持「異步」模式,該定時器每秒喚醒一次以更新某些機器並更新一大組數據結構和全局變量。因爲VB6真的是單線程的,即使它可以通過在子程序調用之間或通過DoEvent調用事件來執行事件來模擬異步性。如何重新設計這個以使其線程安全?

在C#中,我們有真正的異步處理,因爲System.Timers.Timer我在一個正在運行的新線程中使用結果。不幸的是,這意味着我們得到了線程衝突,因爲計時器線程試圖操作用戶的應用程序線程也觸摸的項目。

警告:數據由計時器線程操作是大多樣 - 數以百計的不同的數據結構,變量和標誌被用遍了其他DLL的調用,所以它不是實際做只是幾個關鍵部分來保護它。

理想情況下,將計時器代碼的工作移動到將在應用程序線程中運行的調用中,並使用計時器以某種方式觸發該調用將是最容易的。但是如何?

我們的測試應用此DLL 發生是一個WPF程序,所以可能我們可以使用某種DispatchTimer線程來解決這個在我們的測試情況,但我們沒有依據的假設,該用戶的應用程序是基於調度程序的,所以它不是一個通用的解決方案。

+0

「只保留幾個關鍵部分是不現實的。」爲什麼不?這似乎不會比舊的實現更糟糕,所有訪問都來自單個線程。我想你可以實現你自己的生產者/消費者調度器 - 定時器是生產者,消費者是實際工作完成的線程 - 從而不依賴於事件分派(例如WPF或WinForms)。但是,除非一個代碼示例說明了爲什麼單個頂級鎖定不充分,否則很難理解其他替代方案會更好。 – 2014-11-21 19:34:01

+0

因爲在DLL中有許多不同的變量和結構,有時用於計算,有時用於寫出文件和格式以供顯示。這用於工業機器有幾百個參數。 DLL本身有大約100個入口點。因此,如果我們必須在關鍵部分中包含觸及其中的任何代碼,那將是一場噩夢。我認爲保持當前定時器中的代碼基本完好,但找到一種方法在應用程序線程中執行它會更容易。我只是不知道如何。 – user316117 2014-11-21 19:47:10

+0

沒有代碼示例,就不可能說出可以添加到您的應用程序線程中的什麼東西,以允許計時器在該處引發某些內容。您可以使用一些技巧,包括使用自定義的'SynchronizationContext'或者只編寫自己的委託隊列來調用。但是您仍然需要讓應用程序線程定期從隊列中消費某些東西。這是否可行取決於該線程正在做什麼。無論如何,你花時間在C#中重新實現了整個事情;爲什麼不訪問每個公共成員並添加同步? – 2014-11-21 19:52:44

回答

0

如果定時器在每次關閉時都不需要處理數據,則可以使用Monitor.TryEnter(),並只聲明一個同步對象。就像是。

private readonly object _syncObject = new object(); 

..... 

private void ProcessingMethod(){ 

    if(Monitor.TryEnter(_syncObject)) 
    { 
     try{ 
       //do some processing. 
     } 
     finally{ 
      Monitor.Exit(_syncObject); 
     } 
    }   
} 

如果計時器確實必須在每個間隔處理,那麼您需要使用lock語句。如果它的處理時間很長,則需要編寫邏輯來防止計時器積壓。

我認爲正在處理的對象不是在WPF上下文中創建的,或者出於某種原因,您無法將上下文傳遞給處理方法。

+0

請記住,這是從VB6轉換而來的,VB6不是面向對象的語言。只有很多變量和數據結構代表設備的參數。我可以將它們全部包裝在一個類中並實例化該類,以便擁有一個對象。那麼在DLL的每個入口點上,我會抓取同步對象並在我退出時釋放它? – user316117 2014-11-21 20:15:52