none
請問關於 Invoke 的問題 RRS feed

  • 問題

  • 由於程式碼很長, 我大概敘一下我的做法及遇到的問題,

    我有一個 Class , 用到某 DLL 的 Function 來處理我的 Data , 並以 RaiseEvent 的方式回傳 Image 及其他變數,
    利用這個 Function 處理 Data 的 Sub 是以另一個 Thread 跑 Do ~ Loop 不停地處理,
    這個 Function 只有在這個 Thread 使用, 也就是不會有不同 Thread 同時使用這個 Function 問題,

    這個 Function 需要代入幾個 Point , 分別指向幾個 Byte Array,
    我試了兩種做法, 一種是用 Marshal.AllocHGlobal(Size) 配置這些 Memory ,
    分別再用 Marshal.Copy 填入 Byte Array 的 Data,
    代入 IntPtr , 處理完再用 Marshal.Copy 取出 Data , 最後再 Marshal.FreeHGlobal

    另一種做法, 是沿用 VB6 的方式, 在 Declare 時, 宣告為 ByRef 參數 As Byte,
    然後先將代入的 Byte Array 各 Clone 一份複本, 再用這複本的 Byte(0) 代入,

    兩種都能順利得到結果, 也都不會有其他 Thread 同時存取這些 Byte Array 的問題,

    在 Form 裡, New 一個這個 Class 的實體, 開始跑那個 Thread, 由於是在另一個 Thread 裡 RaiseEvent 的,
    沒辦法直接存取 Form 的 PictureBox, 所以我開了一個 Buffer,
    在事件程序裡, 將 Data 放到 Buffer 中, 由 Form 裡的 Timer 或 Do ~ Loop 再來處理 Buffer 內的 Data,
    以變數記錄是否更新, 避免 Buffer 同時被存取, 但因為處理 Buffer 的速度, 沒有 Thread RaiseEvent 的速度快,
    所以會有些 Data 還沒被處理過就被覆蓋了(若用 Queue 會愈長愈大),

    所以我用了 Invoke 的方式,
    一開始, 我是在事件程序中, 用 Form.Invoke 去跑一個 Delegate Sub,
    由於 Thread 裡處理的速度很快, Invoke 會造成嚴重的 Delay 現象,
    後來把 Invoke 移到 Class 裡, 做法如下,
    Class 繼承 System.ComponentModel.Component,
    宣告一個 _SyncObject As System.ComponentModel.ISynchronizeInvoke,
    在 Form 建立實體後, 再透過 Property 的 Set , 讓 _SyncObject 等於這個 Form,
    然後在 Class 宣告 RaiseEvent 用的 Delegate Sub, 在 Thread 裡頭再用 _SyncObject.Invoke,
    這樣做就改善了嚴重 Delay 的現象, 但還是會有一點, 感覺就像 Invoke 要執行的東西太多, 在系統有個 Queue 在排隊,
    如果在 Thread 的 Do ~ Loop 裡, 用 Sleep 1,就不會有 Delay 了,
    但是處理 Data 的速度又降下許多, 所以不能用 Sleep, 當然更不能用 Application.DoEvents,
    而且使用 Invoke 之後, 遇到了一個問題,
    我在呼叫那個處理 Data 的 Function 時, 會不定時遇到 AccessViolationException,
    錯誤訊息為 嘗試讀取或寫入受保護的記憶體。這通常表示其他記憶體已損毀。
    這是用了 Invoke 之後才產生的, 是否 Invoke 會去影響到其他的記憶體?

    請問有人知道怎麼解決這兩個問題嗎? 謝謝
    2007年7月6日 上午 01:41

解答

  • 也有可能你目標的記憶體區塊已被釋放,造成你要寫入一個未受允許的區域或是 Null 參照。

    建議你檢查你的呼叫流程與釋放的順序。

     

    另外,我印象中 VB6 的 DLL 不能在多緒 (Multi-Thread) 下跑,只能在多程序 (Multi-Process) 下跑,VB2003 以前允許不安全的呼叫,VB2005 不允許,不能確定由主執行緒 (GUI Thread) 建立的物件能不能給子執行緒 (Work Thread) 呼叫,建議呼叫 VB6 的 DLL 時,不要考慮另建子執行緒呼叫,或著考慮在 VBNET 內重寫該 DLL 。

    2007年7月6日 上午 04:15
    版主

所有回覆

  • 也有可能你目標的記憶體區塊已被釋放,造成你要寫入一個未受允許的區域或是 Null 參照。

    建議你檢查你的呼叫流程與釋放的順序。

     

    另外,我印象中 VB6 的 DLL 不能在多緒 (Multi-Thread) 下跑,只能在多程序 (Multi-Process) 下跑,VB2003 以前允許不安全的呼叫,VB2005 不允許,不能確定由主執行緒 (GUI Thread) 建立的物件能不能給子執行緒 (Work Thread) 呼叫,建議呼叫 VB6 的 DLL 時,不要考慮另建子執行緒呼叫,或著考慮在 VBNET 內重寫該 DLL 。

    2007年7月6日 上午 04:15
    版主
  • 十分感謝大大您的回覆,

    當例外產生時, 我去檢查每一個陣列, 值都是正確的, 如果記憶體區塊已被釋放, 那應該沒辦法再看到正確的值吧?

    另外這個 DLL 以前是用 C++ 封裝給 VB6 用的, 這方面我會再去確認一次, 謝謝您的指點.

    2007年7月6日 上午 04:21
  • 如果原先是 C++ 編譯的,確認編譯時編譯參數有沒有下 /MT

    2007年7月6日 上午 07:48
    版主
  • 經確認之後, 這個 DLL 在 VB2005 ,多緒的情況, 是可以正常使用的,

    小弟再一次嘗試把 Invoke 的部份 mark, 改用直接 RaiseEvent,

    然後在 Form 上的事件程序中, 將 Data 放入 Buffer , 再以另一個 Thread 跑 Loop 去讀取並顯示影像,

    其他部份的程式碼完全沒動到, 這樣子就正常了, 我想應該是 Invoke 的問題吧...

    (但還是只能用 Invoke , 因為顯示影像的速度不夠快, 會導致每隔一段時間遺漏一筆)

    2007年7月9日 上午 01:30