[备忘]不用许可证 多线程直接操作界面组件比如超级列表框的实现

平时多线程来操作界面组件 同时写入或者修改数据  比如常见的把多个线程都把日志同时写入到编辑框 又或者 多个线程同时的修改一个超级列表框上的线程状态和其他信息 这样会出现一个问题 如何避免多个线程同时操作一个组件导致的组件冲突问题 我们常用的是使用许可证来给每个线程规定访问顺序来依次执行 不过这样的调整的确从效率上说很低下

大漠老师使用 发送消息 或者说是使用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库 ()

123

猜你喜欢

转载自www.cnblogs.com/zjl8455482/p/10743580.html