找回密码
 立即注册
查看: 5265|回复: 31

浅谈易语言多线程...

[复制链接]

707

回帖

1万

基友

1万

积分

萨菲尔斯

Rank: 17Rank: 17Rank: 17Rank: 17Rank: 17

发表于 2014-1-17 19:02:01 | 显示全部楼层 |阅读模式
更新完遗忘一时无聊于是写下这篇教程,谈下易语言多线程操作问题..
总所周知,易语言多线程是个很坑爹的玩意,内存回收垃圾,然后我们这种小鸟用多线程就各种错误
码字挺辛苦的,大家体谅下,有些地方为了方便就直接引用其他大神的言论了
另外本人知识有限,错误之处还请大神指教

一、简介
1、线程句柄与线程:
①、关闭线程句柄对线程的运行不会有影响,关闭句柄并不代表结束线程;
②、线程句柄是用于对线程挂起、恢复、结束等操作,线程创建后,都会有一个线程句柄,如果不需要对线程句柄进行操作,建议立即关闭线程句柄;
③、线程句柄必须在适当的时候关闭,否则会造成句柄泄露,但不同于内存泄露。
2、死锁、循环死锁、活锁
①、死锁:线程A占有资源A,线程B占有资源B,线程A申请占有资源B,同时要求占有资源B之后才释放资源A,而线程B申请占有资源A,同时要求占有资源A之后才释放资源B,这样两个线程互相永久等待对方释放资源,这就是死锁。
②、循环死锁:线程A占有资源A,线程B占有资源B,线程C占有资源C,线程A申请占有资源B,同时要求占有资源B之后才释放资源A,而线程B申请占有资源C,同时要求占有资源C之后才释放资源B,线程C申请占有资源A,同时要求占有资源A之后才释放资源C,这样线程互相永久等待对方释放资源,这就是循环死锁。
③、活锁:提交任务之后,任务永远处于等处理状态,这就是活锁。这种情况比较少见,但是出现这种情况,将比死锁更加不易查觉,避免活锁的简单方法是采用先来先处理。









评分

参与人数 2基友 +30 收起 理由
best + 20 支持一下
and1and1 + 10 难得一见的好贴,

查看全部评分

回复

使用道具 举报

707

回帖

1万

基友

1万

积分

萨菲尔斯

Rank: 17Rank: 17Rank: 17Rank: 17Rank: 17

 楼主| 发表于 2014-1-17 19:03:04 | 显示全部楼层
二、注意事项
1、虽然启动线程要比启动进程要快,但是启动线程仍是比较耗时的,因此,不要频繁的启动、退出线程,而是启动线程后将各种任务处理完成后才退出(这种和线程池差不多);
2、对窗口各种组件操作,最好是在创建该窗口的线程上进行操作,如果在其它线程上操作,可能会引起程序出错等情况(该错误是随机出现的)。(未找到直接又安全的调用其他线程创建的组件的方法,有知道的人,麻烦告诉一下,谢谢!)
3、线程运行次序并不是按照我们创建他们时的顺序来运行的,CPU处理线程的顺序也是不确定的。
4、读/写共享资源时一般需要使用许可区,当然,在明知读/写共享资源不会出现错误时,就不需要许可区,这样可提高性能。
5、在编写多线程时,必须以多线程的方式考虑读/写共享资源,以避免出错,不然的话,可能会出现各种问题,如:意外退出、在单核CPU上可以稳定运行的多线程程序一到多核CPU上运行就出错。
6、线程中如果需要使用COM对象时,要需将COM对象初始化。
7、结束线程时,应该使用正常的控制代码使线程退出,强烈反对使用强制结束线程(),该命令极可能造成一些资源未释放,从而导致程序的不稳定。
8、线程不能频繁的发消息给窗口,频繁的发消息给窗口,可能会造成窗口响应其他事件的缓慢,也是就让人感觉程序运行很慢;
9、要注意避免各种死锁、活锁发生,确实无法避免的话,就只能想法解锁,同时得注意解锁时引发的新的问题。

回复 支持 反对

使用道具 举报

707

回帖

1万

基友

1万

积分

萨菲尔斯

Rank: 17Rank: 17Rank: 17Rank: 17Rank: 17

 楼主| 发表于 2014-1-17 19:03:36 | 显示全部楼层
三、多线程的误区
1、使用处理事件()。非窗口的线程是没有窗口消息循环,而处理事件()命令是用于消息循环,因此在非窗口的线程上是不必加入“处理事件()”命令;
2、线程越多越好。线程并非越多越好,有些人将单线程改成多线程后,发现程序能处理更多的任务了,实际上这种方法是建立别的程序的痛苦之上(当然系统有空闲资源就并当别论了),别的程序可能因此而变慢。并且,线程数过多,会使CPU在线程间切换的开销增加,因而使速度变慢,降低系统性能。在一些阻塞式、耗资源少的线程上需要适当的增加线程数量,以免程序无响应。
四、许可区
1、许可区(一般称为临界区),不论是硬件许可资源,还是软件许可资源,多个线程必须互斥地对它进行访问,每个线程中访问许可资源的那段代码称为许可区。
2、注意事项:
①、如果有若干线程要求进入许可区,一次仅允许一个线程进入;
②、任何时候,处于许可区内的线程不可多于一个。如已有线程进入自己的许可区,则其它所有试图进入许可区的线程将被挂起,并一直持续到进入许可区的线程退出;
③、进入一个空闲的许可区时,耗时极少,但是进入一个需等待的许可区时,耗时相对较长,因此需要避免经常出现进入需等待的许可区;
④、创建后许可区,在不再使用时,需要将其删除;
⑤、在使用许可区时,应尽量减少许可区内代码,避免使用需长时间处理的代码,使进入许可区的线程能尽快退出,以便其它线程能进入许可区;
⑥、避免将整个线程处于许可区内,尽管它不会出错,但是由于后来要求进入许可区的线程全部会被挂起,也就会出现虽然是多线程,但实际是以单线程方式执行;
⑦、访问相同的许可资源时,必须是以相同的许可区进入访问,以不同的许可区进入访问将可能会使许可区变的无意义(我在这个坑里蹲了很久,郁闷啊!)。
3、许可区缺点
①、无法侦测某个许可区是否可进入。
五、线程同步
1、临界区(CriticalSection)
易语言中称为许可区,这种速度最快,但只能用于本进程的线程同步;
2、事件(Event)
事件可以跨进程使用,它有两种状态、两种类型:有信号状态和无信号状态、手动重置事件和自动重置事件。手动重置事件被设置为有信号状态后,会唤醒所有等待的线程,而且一直保持为有信号状态,直到程序重新把它设置为无信号状态。自动重置事件被设置为有信号状态后,会唤醒“一个”等待中的线程,然后自动恢复为无信号状态。
3、互斥器(Mutex)
互斥器的功能和临界区很相似,互斥器所花费的时间比临界区多的多,同时它可以跨进程使用。等待一个被锁住的互斥器可以设定超时退出,不会像临界区那样无法得知临界区的情况,而一直死等。
4、信号量(Semaphore)
与临界区相比,它信号量可以跨进程使用,可以设定同时进入资源总数。
六、线程通信
线程通信是一般都是需要配合线程同步来使用:
1、使用全局变量进行通信,推荐使用这种方法,该是最快、最方便的通信方式;
2、使用消息通信(需要有消息队列才能使用);
3、使用Socket进行通信(可以跨计算机使用);
回复 支持 反对

使用道具 举报

5390

回帖

30万

基友

29万

积分

天下一番

爱苍海,爱大家!

Rank: 18Rank: 18Rank: 18Rank: 18Rank: 18

会员纪念勋章伯爵荣耀

发表于 2014-1-17 19:14:32 | 显示全部楼层
不错
对论坛有任何建议或者意见,请到版务专区发帖。。谢谢
回复 支持 反对

使用道具 举报

707

回帖

1万

基友

1万

积分

萨菲尔斯

Rank: 17Rank: 17Rank: 17Rank: 17Rank: 17

 楼主| 发表于 2014-1-17 19:18:06 | 显示全部楼层
以上都是某易语言大神的讲解。。下面得我自己辛苦码字了。。。

首先你的支持库得是没有问题的。。(之前一直用个有bug的支持库。。郁闷)
多线程安全的核心支持库 http://bbs.eyuyan.com/read.php?tid=316079
一定要用没有问题的核心支持库。。注意,是核心支持库,不是多线程支持库,原来那个有问题即使不操作全局变量一样会出问题
多少人是百度搜索下载个破解版易语言一直用个有bug的支持库。。

我相信很多人的问题都是提示XXXXX内存访问错误.
我们就了解一下这个错误怎么产生
线程a对变量操作的时候b也操作,a改写了文本申请了新的内存地址同时修改了指针然后释放了原指针,但是b读取了原指针需要读取数据的时候,a已经把指针释放了.然后就各种内存错误。。
值得注意的是,一边读一边执行写操作也不行,
你线程a对全局文本变量赋值"1234567",那么在线程b对全局变量读写前,会经过至少两个步骤
1、获取内存区域大小,假设是8的话
2、读取内容 而实际上进行到读取第8位的时候,你线程a重新给全局文本变量赋值“123456”,这就导致“踏空”,于是程序直接崩溃。而对于长度固定的变量进行读写时,就算读取到的数据是错误的,也不至于崩溃(当然,这不是说写程序可以不严谨,宁愿读错也不加保护,当你的程序足够大的时候,一点点小小的问题都会导致致命错误)
关于只读不写操作。。我认为不需要加许可证。。也有大神说要加。这个不清楚。。
另外尽量用整数型变量。。下面会讲解
http://blog.csdn.net/q349980363/article/details/8012495
上面那个是某大神列出的易语言数据类型内存分布格式
里面几个没必要说明的可以进行多线程读写操作,其他以外的都有可能引发问题


回复 支持 反对

使用道具 举报

707

回帖

1万

基友

1万

积分

萨菲尔斯

Rank: 17Rank: 17Rank: 17Rank: 17Rank: 17

 楼主| 发表于 2014-1-17 19:23:44 | 显示全部楼层
关于易语言控件操作问题。。
控件操作一定要加许可证。。这个大家都知道。。
但这样依然会不那么稳定。。
我建议调用标签反馈事件。。虽然比较卡
不过我的遗忘是先获取控件句柄,然后用api和发送信息在线程里面操作,线程里面发送信息应该用SendMessage()、PostMessage()
附上一张我遗忘登录操作控件截图
QQ图片20131221100430.jpg
我个人认为这样再加上许可证。。算是比较稳定
回复 支持 反对

使用道具 举报

707

回帖

1万

基友

1万

积分

萨菲尔斯

Rank: 17Rank: 17Rank: 17Rank: 17Rank: 17

 楼主| 发表于 2014-1-17 19:35:44 | 显示全部楼层
关于易语言启动线程,结束线程问题

启动我建议用官方的多线程支持库。。虽然最终都是调用CreateThread..
结束线程我强烈反对强制结束线程,无论是什么支持库,什么多线程模块,都是调用TerminateThread,应该尽量避免这样结束线程,我认为应该在线程内退出
顺便提一下。。不知道是不是风列的原因。。很多人都喜欢用多线程支持库1.1,里面有个退出线程命令我反对,因为这个命令是调用ExitThread,该函数将终止线程的运行,并导致操作系统清除该线程使用的所有操作系统资源。但是有一些资源将不被撤消。由于这个原因,最好从线程函数返回,而不是通过调用ExitThread 来返回。
关于句柄问题,不需要一定要CloseHandle,防止句柄泄露,不过我一般创建线程都不提供句柄参数,让系统自动销毁
因为我很少用到TerminateThread,当然写发帖器这些需要验证码操作就不可避免用到句柄。。为了方便。。多少机器跟了风列的思路。。SuspendThread线程,输入验证码又ResumeThread...




回复 支持 反对

使用道具 举报

707

回帖

1万

基友

1万

积分

萨菲尔斯

Rank: 17Rank: 17Rank: 17Rank: 17Rank: 17

 楼主| 发表于 2014-1-17 19:39:13 | 显示全部楼层
这里讲解下原子操作
  1. .版本 2
  2. .支持库 spec
  3. .程序集 程序集1
  4. .程序集变量 sum, 整数型
  5. .程序集变量 locked, _原子锁类
  6. .子程序 _启动子程序, 整数型, , 本子程序在程序启动后最先执行
  7. .局部变量 i, 整数型
  8. .局部变量 idz, 整数型, , "0"
  9. .局部变量 time, 整数型
  10. ' 转换类et调试 ()
  11. 重定义数组 (idz, 假, 10)
  12. time = 取启动时间 ()
  13. .计次循环首 (10, i)
  14.     Thread.启动 (&累加, , idz [i])
  15. .计次循环尾 ()
  16. .计次循环首 (10, i)
  17.     Thread.等待 (idz [i])
  18. .计次循环尾 ()
  19. 调试输出 (取启动时间 () - time, sum)
  20. 返回 (0)  ' 可以根据您的需要返回任意数值
  21. .子程序 累加
  22. .局部变量 i
  23. .变量循环首 (1, 9999999, 1, i)
  24. .变量循环尾 ()
  25. locked.加减 (sum, i)
复制代码
这也是我为什么提倡大家尽量用整数型变量原因了
整数型变量是固定长度,而且可以加原子锁,速度远快过许可证,不信自己测试


回复 支持 反对

使用道具 举报

707

回帖

1万

基友

1万

积分

萨菲尔斯

Rank: 17Rank: 17Rank: 17Rank: 17Rank: 17

 楼主| 发表于 2014-1-17 19:48:08 | 显示全部楼层
关于易语言双核亲和性问题
SetProcessAffinityMask这个命令是把程序绑定在一个核上。。。我个人感觉没什么用,而且失去了多核的优势
提到SetProcessAffinityMask就不得不提到 SetThreadIdealProcessor
SetProcessAffinityMask是自动切换线程到同核内执行,后者是切换到其他空闲执行这就是区别,主要区别是充分利用所有多核CPU
  1. .版本 2

  2. .DLL命令 SetProcessAffinityMask, 整数型, , , 公开, 设置CPU亲和性 进程句柄 返回CPU号
  3. .参数 hProcess, 整数型
  4. .参数 hProcess, 整数型

  5. .DLL命令 SetThreadAffinityMask, 整数型, , , 公开, 设置CPU亲和性 线程句柄
  6. .参数 hProcess, 整数型
  7. .参数 Mask, 整数型

  8. .DLL命令 GetCurrentProcess, 整数型, , , 公开, 获取当前进程的一个伪句柄 一般是-1

  9. .DLL命令 GetCurrentThread, 整数型, , , 公开, 获取当前线程的一个伪句柄 一般是-2
复制代码
回复 支持 反对

使用道具 举报

707

回帖

1万

基友

1万

积分

萨菲尔斯

Rank: 17Rank: 17Rank: 17Rank: 17Rank: 17

 楼主| 发表于 2014-1-17 19:57:28 | 显示全部楼层
最后是一些细节问题..
多线程写法各种各样,我比较推荐飞龙那种
当然无论你怎么写,都应该尽量避免操作全局变量和控件

这里提一下
很多人看到多线程出现内存错误
就想办法去优化内存
使用各种内存优化等等。。爆吧界应该不少人这样
所谓的内存优化模块应该都是调用SetProcessWorkingSetSize,
这个命令就是将程序所使用的物理内存尽量地向虚拟内存中压
表面上看,物理内存占用确实是少了.
但是,一旦程序需要使用到已经被强行压到虚拟内存(也就是硬盘页面文件)中的内容时,又得重新从虚拟内存里读出来.
目前最好的硬盘速度比起内存来说那都至少是慢了几十上百倍,于是此招就会造成当前程序的运行效率严重下降,同时因为频繁读硬盘,占用了本来就不多的带宽,搞得整个系统的运行效率都下降了.
我觉得这个命令应该在程序空闲时使用。。切忌不要频繁调用
爆吧界很多机器都是从一开始就优化,隔几秒优化一次到结束。。是利是弊最终还是取决于你的程序

我个人反对调用模块,因为你不知道模块里面有什么问题
这里提下我遗忘在xp系统崩溃问题
因为百度坑爹的验证码在图片框无法显示,所以需要转换图片
然后我用了精易模块那个图片转换命令,但这个命令在xp系统是比较容易崩溃
但我技术有限,目前无法解决


回复 支持 反对

使用道具 举报

707

回帖

1万

基友

1万

积分

萨菲尔斯

Rank: 17Rank: 17Rank: 17Rank: 17Rank: 17

 楼主| 发表于 2014-1-17 20:08:05 | 显示全部楼层
本帖最后由 逆风而行 于 2014-1-21 18:29 编辑

这楼说下爆吧界一些机器的一些问题。。
我个人是推荐用控制台输出
不知道控制台算不算控件
但觉得比这么频繁操作编辑框好多
我发现爆吧界很多机器信息输出都不喜欢加许可证
这不单单是输出乱成一片问题,很容易造成程序错误

很多机器都喜欢在程序启动后使用一个线程不断保存信息,这个我强烈反对
不谈操作控件的问题
易语言本身的分割文本命令、读写配置项()命令、取现行时间()
这些命令在多线程会出问题
那些输出用取现行时间的还是改为用api吧。。
另外有些编码转换模块会出问题。。具体百度

我相信很多机器都用到延迟这个命令
在多线程中我不建议用这个
我建议用延时
“延迟”,在执行时允许用户执行其它的操作(如:单击按钮等);
“延时”,在执行时程序会进入“假死“状态,用户的其他操作程序将无法响应,必须等到语句执行结束才能恢复。
据我测试,延时在多线程中也可以操作控件
或者大家可以用什么高精度延时也行

关于使用控件属性问题,我个人不喜欢
不论是否稳定,但可以使用变量的速度肯定快过使用控件属性
我的遗忘是启动后直接给变量赋值
大家的变量是启动后赋值还是任务前赋值取决于你的软件怎么写

这篇教程到此就结束了。。错误之处还请大神指教。。我的遗忘到现在也不是那么稳定

最后说下IsBadReadPtr 和 IsBadWritePtr
这两个命令一个判断是否可读,一个判断是否可写。。
由于我本人也没研究透。。所以就不介绍了
@万能的飞龙 求解答。。



回复 支持 反对

使用道具 举报

707

回帖

1万

基友

1万

积分

萨菲尔斯

Rank: 17Rank: 17Rank: 17Rank: 17Rank: 17

 楼主| 发表于 2014-1-17 20:09:21 | 显示全部楼层
附上c++多线程教程
http://bbs.eyuyan.com/read.php?tid=300331
看了对易语言多线程也是有很大帮助的
回复 支持 反对

使用道具 举报

1万

回帖

2万

基友

5万

积分

苍海之魂

S̲̅F

Rank: 13Rank: 13Rank: 13Rank: 13

苍海的女仆会员纪念勋章周年纪念勋章

发表于 2014-1-17 20:27:59 | 显示全部楼层
于是,讲解完成。我是沙发支持个
回复 支持 反对

使用道具 举报

179

回帖

68

基友

1216

积分

通神3段 Lv.6

Rank: 3Rank: 3

发表于 2014-1-17 20:51:47 | 显示全部楼层
幸苦你了
回复 支持 反对

使用道具 举报

2034

回帖

2万

基友

2万

积分

仙人7层 Lv.16

Invincible

Rank: 10Rank: 10Rank: 10

发表于 2014-1-17 20:56:55 来自手机 | 显示全部楼层
看这样e还没汉化到家→_→
回复 支持 反对

使用道具 举报

2034

回帖

2万

基友

2万

积分

仙人7层 Lv.16

Invincible

Rank: 10Rank: 10Rank: 10

发表于 2014-1-17 20:58:36 来自手机 | 显示全部楼层
顺便求12l的附件 我没有那个论坛的帐号→_→
回复 支持 反对

使用道具 举报

17

回帖

76

基友

72

积分

凡人2阶 Lv.2

Rank: 1

伯爵荣耀

发表于 2014-1-17 21:02:31 | 显示全部楼层
嗯,回你十五字,算算有木有十五个字
回复 支持 反对

使用道具 举报

17

回帖

76

基友

72

积分

凡人2阶 Lv.2

Rank: 1

伯爵荣耀

发表于 2014-1-17 21:02:41 | 显示全部楼层
嗯,回你十五字,算算有木有十五个字
回复 支持 反对

使用道具 举报

17

回帖

76

基友

72

积分

凡人2阶 Lv.2

Rank: 1

伯爵荣耀

发表于 2014-1-17 21:02:50 | 显示全部楼层
嗯,回你十五字,算算有木有十五个字
回复 支持 反对

使用道具 举报

1220

回帖

5118

基友

4428

积分

通神6段 Lv.9

Rank: 5Rank: 5

发表于 2014-1-17 22:07:39 | 显示全部楼层
什么意思
回复 支持 反对

使用道具 举报

285

回帖

1234

基友

1097

积分

通神3段 Lv.6

Rank: 3Rank: 3

发表于 2014-1-17 23:44:22 | 显示全部楼层
围观
回复 支持 反对

使用道具 举报

477

回帖

1537

基友

3190

积分

通神5段 Lv.8

Rank: 4

发表于 2014-1-18 00:18:44 | 显示全部楼层
楼主到底建议用什么来关闭线程呢?API函数还是自带的命令
回复 支持 反对

使用道具 举报

477

回帖

1537

基友

3190

积分

通神5段 Lv.8

Rank: 4

发表于 2014-1-18 00:29:18 | 显示全部楼层
那个验证码有API可以解决的
回复 支持 反对

使用道具 举报

707

回帖

1万

基友

1万

积分

萨菲尔斯

Rank: 17Rank: 17Rank: 17Rank: 17Rank: 17

 楼主| 发表于 2014-1-18 08:02:05 来自手机 | 显示全部楼层
and1and1 发表于 2014-1-18 00:18
楼主到底建议用什么来关闭线程呢?API函数还是自带的命令

我建议在线程内自动退出。。
回复 支持 反对

使用道具 举报

707

回帖

1万

基友

1万

积分

萨菲尔斯

Rank: 17Rank: 17Rank: 17Rank: 17Rank: 17

 楼主| 发表于 2014-1-18 08:04:13 来自手机 | 显示全部楼层
and1and1 发表于 2014-1-18 00:29
那个验证码有API可以解决的

精易模块图片转换也是一堆api。。感觉无论精易模块还是超级模块,图片转换都不稳定。。动态创建图片框虽然可以不需要转换。。但发帖时图片框抖得那个厉害不敢用。。
回复 支持 反对

使用道具 举报

949

回帖

4092

基友

4865

积分

通神6段 Lv.9

Rank: 5Rank: 5

伯爵荣耀

发表于 2014-1-18 08:53:01 | 显示全部楼层
看不懂啊看不懂
回复 支持 反对

使用道具 举报

9012

回帖

2万

基友

2万

积分

仙人7层 Lv.16

Rank: 10Rank: 10Rank: 10

伯爵荣耀

发表于 2014-1-18 12:28:49 | 显示全部楼层
好顶赞
回复 支持 反对

使用道具 举报

477

回帖

1537

基友

3190

积分

通神5段 Lv.8

Rank: 4

发表于 2014-1-18 13:43:08 | 显示全部楼层
逆风而行 发表于 2014-1-18 08:04
精易模块图片转换也是一堆api。。感觉无论精易模块还是超级模块,图片转换都不稳定。。动态创建图片框虽然 ...

用来如此,
回复 支持 反对

使用道具 举报

215

回帖

107

基友

896

积分

通神2段 Lv.5

Rank: 3Rank: 3

发表于 2014-1-19 13:10:13 | 显示全部楼层
支持下!!!
回复 支持 反对

使用道具 举报

434

回帖

1923

基友

4544

积分

通神6段 Lv.9

Rank: 5Rank: 5

伯爵荣耀

发表于 2014-1-20 11:34:54 | 显示全部楼层
强势插入
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|苍海国际 ( 鲁ICP备13020644号-1 )

GMT+8, 2024-12-26 18:36 , Processed in 0.142512 second(s), 37 queries .

Powered by Discuz! Theme By eRic Modified by 4bpa

© CangHai International We Do Our Rights!

返回顶部