[備忘]不用許可證 多線程直接操作界面組件比如超級列表框的實現


平時多線程來操作界面組件 同時寫入或者修改數據  比如常見的把多個線程都把日志同時寫入到編輯框 又或者 多個線程同時的修改一個超級列表框上的線程狀態和其他信息 這樣會出現一個問題 如何避免多個線程同時操作一個組件導致的組件沖突問題 我們常用的是使用許可證來給每個線程規定訪問順序來依次執行 不過這樣的調整的確從效率上說很低下

大漠老師使用 發送消息 或者說是使用window消息機制來實現不加許可證的同時修改界面組件的思路非常好 

511遇見老師也對這個思路進行了深度解析 已經非常的詳細了http://www.511yj.com/eyuyan-dthread-31.html

這個方式的情況

優點

(1)不需要加許可證 效率比許可證明顯提高

(2)就算使用了許可證 多線程下在對不是在本線程內創建的com對象或者組件進行操作 依然會存在崩潰的可能性 而這個方法能夠徹底的避免這個情況 所以它基本上是唯一的選擇 許可證輔助 因為我們操作組件 其實只是觸發了組件的某個指定事件 這事件的處理依然還是在主線程里面發生的 而不是在多線程里面 或者說是發生在界面上的某個組件的事件里面 這樣就符合了 哪個線程創建的com對象或者組件就在哪個線程里面處理的原則

缺點

(1)需要對window消息機制有點了解 起碼對要用到的3個系統API的功能和使用方法有一定了解

(2)如果間隔太短 或者過於頻繁的寫入 依然會造成界面陷入短暫卡死的情況 我建立了20個線程 每個線程以間隔10毫秒的方式對一個超級列表框進行修改對應的記錄信息 結果就是時常陷入假死 但是沒有真正徹底卡死  把線程內間隔10毫秒改為100毫秒 基本就看不到卡死的情況


小知識:個人理解下的全局變量的類型選擇偏向

如果可能 盡量選擇一些數值類型 或者邏輯類的全局變量 ,一些文本型的全局變量 在被多線程使用的時候可能存在一些極端情況 因為數值類型或者邏輯型的 在內存中占據的空間是固定大小的 而文本型卻是根據內容多寡而在內存占據的空間會變化的  比如目前a這個全局變量的內容占據16個字節 1號線程訪問按照16字節的大小讀取 但是可能在這瞬間 因為全局變量的讀取並沒有許可證一類的機制 所以可能在訪問的一時間 2號線程已經把a這個全局變量的內容改變了 現在占據8個字節 那么這個時候1號線程依然按照16字節讀取 就會造成線程沖突導致崩潰

結論:如果可能 全局變量盡量選擇一些內容長度固定的變量類型來實現


個人理解的發送消息訪問組件的基本原理

首先要對window消息機制有點了解 我們平時 比如窗口載入事件  窗口銷毀事件 按鈕點擊事件 這些東西都是屬於window消息機制  系統一直在維護一個window消息列表  每當我們操作觸發了一個窗口事件 會自動把該事件的消息加入這個window消息列表 ,然后系統一直在從上到下依次一個個的執行列表里面的項 根據傳遞的事件的信息 來決定用什么函數來執行這個事件。但是我們可以通過3個系統API  可以改寫指定的某個事件對應的系統默認的處理函數  我們可以把這個默認處理函數修改為我們自己寫的函數 每次觸發了指定事件后  處理方式是按照我們的指定函數來執行而不是系統默認的處理函數 因為觸發的事件是系統讀取window消息列表自動從上到下依次執行 不會產生沖突 自動解決了多線程同時操作導致的問題

 

需要用到的系統API(可以直接從易語言助手里面找到對應的)

.版本 2

.DLL命令 CallWindowProcA, 整數型, "user32.dll", "CallWindowProcA", , 呼叫窗口函數地址
    .參數 lpPrevWndFunc, 整數型, , 前一窗口函數地址
    .參數 hWnd, 整數型, , 窗口句柄
    .參數 Msg, 整數型, , 消息值
    .參數 wParam, 整數型, , 附加參數1
    .參數 lParam, 整數型, , 附加參數2

.DLL命令 SetWindowLongA, 整數型, "user32.dll", "SetWindowLongA", , 改變指定窗口的屬性,函數也將指定的一個32位值設置在窗口的額外存儲空間的指定偏移位置。
    .參數 hWnd, 整數型, , 窗口句柄及間接給出的窗口所屬的類。
    .參數 nIndex, 整數型, , 指定將設定的大於等於0的偏移值。
    .參數 dwNewLong, 整數型, , 指定的替換值。

.DLL命令 GetWindowLongA, 長整數型, "user32.dll", "GetWindowLongA", , 獲得有關指定窗口的信息,函數也獲得在額外窗口內存中指定偏移位地址的32位度整型值
    .參數 hWnd, 整數型, , 窗口句柄及間接給出的窗口所屬的窗口類。
    .參數 nIndex, 整數型, , 指定要獲得值的大於等於0的值的偏移量。

GetWindowLongA 是用來獲取某個窗口句柄的所有事件對應的系統默認的處理函數的入口地址

SetWindowLongA用用來設置某個窗口句柄的所有事件對應的我們自己定義的處理函數

CallWindowProcA 是用來單獨運行某個窗口句柄的所有事件對應的系統默認的處理函數

基本用法

1.設置2個全局變量來獲取當前窗口句柄(窗口句柄)和當前窗口句柄的所有事件對應的系統默認處理函數的入口地址(窗口過程

2 通過3個API 把系統默認的當前窗口的所有事件對應的處理函數變為我們自己的處理函數,並且這個改變必須在主線程里面 不能再其他線程里 那樣是無效的 一般都是放在UI載入事件里面

.版本 2
.支持庫 iext

.子程序 __啟動窗口_創建完畢

日志_初始化 ()
窗口句柄 = _啟動窗口.取窗口句柄 ()
窗口過程 = GetWindowLongA (窗口句柄, -4)
SetWindowLongA (窗口句柄, -4, 到數值 (&WindowProc))

.子程序 WindowProc, 整數型
.參數 hwnd, 整數型
.參數 msg, 整數型
.參數 wparam, 整數型
.參數 lparam, 整數型

.如果真 (msg = 1666)
    ' 調試輸出 (“[” + 到文本 (msg) + “]-[” + 到文本 (wparam) + “]-[” + 到文本 (lparam) + “]”)
    _啟動窗口.超級列表框1.置標題 (wparam - 1, 0, 到文本 (大漠多線程數組 [wparam].id))
    _啟動窗口.超級列表框1.置標題 (wparam - 1, 1, 大漠多線程數組 [wparam].name)
    _啟動窗口.超級列表框1.置標題 (wparam - 1, 2, 到文本 (大漠多線程數組 [wparam].age))
    _啟動窗口.超級列表框1.置標題 (wparam - 1, 3, 大漠多線程數組 [wparam].address)
    _啟動窗口.超級列表框1.置標題 (wparam - 1, 4, 大漠多線程數組 [wparam].備注)
.如果真結束
返回 (CallWindowProcA (窗口過程, hwnd, msg, wparam, lparam))

 

3.就是每個線程都使用 發送消息函數來觸發把全局線程數組的內容寫入到超級列表框對應的記錄里面 其中 發送消息第一個參數 只要是超出1024的都屬於自定義事件 可以隨便選擇 我這里選了一個1666  第二個和第三個參數其實是隨意的 我這里就是第二個參數來存儲 大漠多線程數組的標識 也是線程的標識 第三個是操作模式 是要把大漠多線程數組內容更新到超級列表框還是把超級列表框對應記錄的內容全部重置或者是其他的操作 等等

.版本 2

.子程序 線程_主線程
.參數 大漠多線程數組標識, 整數型
.局部變量 i, 整數型

線程_初始化COM庫 ()
置隨機數種子 ()

.計次循環首 (1000000, i)
    大漠多線程數組 [大漠多線程數組標識].id = 取隨機數 (1, 100)
    大漠多線程數組 [大漠多線程數組標識].name = 文本_取隨機漢字 (3)
    大漠多線程數組 [大漠多線程數組標識].age = 取隨機數 (1, 100)
    大漠多線程數組 [大漠多線程數組標識].address = 文本_取隨機漢字 (5)
    大漠多線程數組 [大漠多線程數組標識].備注 = 文本_取隨機漢字 (10)

    _啟動窗口.發送信息 (1666, 大漠多線程數組標識, 0)  ' 第一個參數固定為1666  第二個是 第三個是操作模式 0是更新 1還是清空
    輔助延時 (100)
.計次循環尾 ()
線程_取消COM庫 ()

補充知識:個人理解的關於窗口觸發自定義事件的2個函數  投遞消息() 和 發送消息()

投遞消息() 就是觸發完消息后 就不等反饋信息就直接結束了 也可以稱之為異步

發送消息() 就是發送消息后觸發完事件 要等操作完畢有反饋回來才會徹底結束

結論:推薦使用投遞消息 雖然會操作和界面反應有點延遲 但是速度快

_啟動窗口.投遞信息 (1666, 大漠多線程數組標識, 0)


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM