浅谈多线程、许可区编程.
谈谈我自己对多线程上的理解,因本人知识有限,如有不对之处请各位高手多多指正、补充,同时,在多线程上有问题而不能解决的易友,可以将自己问题提出,并附上源码(不愿提供源码别找我,我不是神,无法猜测出问题所在),我将会一一测试,并在本人能力范围内解决:
一、简介
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,这样线程互相永久等待对方释放资源,这就是循环死锁。
③、活锁:提交任务之后,任务永远处于等处理状态,这就是活锁。这种情况比较少见,但是出现这种情况,将比死锁更加不易查觉,避免活锁的简单方法是采用先来先处理。
二、注意事项
1、虽然启动线程要比启动进程要快,但是启动线程仍是比较耗时的,因此,不要频繁的启动、退出线程,而是启动线程后将各种任务处理完成后才退出(这种和线程池差不多);
2、对窗口各种组件操作,最好是在创建该窗口的线程上进行操作,如果在其它线程上操作,可能会引起程序出错等情况(该错误是随机出现的)。(未找到直接又安全的调用其他线程创建的组件的方法,有知道的人,麻烦告诉一下,谢谢!)
3、线程运行次序并不是按照我们创建他们时的顺序来运行的,CPU处理线程的顺序也是不确定的。
4、读/写共享资源时一般需要使用许可区,当然,在明知读/写共享资源不会出现错误时,就不需要许可区,这样可提高性能。
5、在编写多线程时,必须以多线程的方式考虑读/写共享资源,以避免出错,不然的话,可能会出现各种问题,如:意外退出、在单核CPU上可以稳定运行的多线程程序一到多核CPU上运行就出错。
6、线程中如果需要使用COM对象时,要需将COM对象初始化。
7、结束线程时,应该使用正常的控制代码使线程退出,强烈反对使用强制结束线程(),该命令极可能造成一些资源未释放,从而导致程序的不稳定。
8、线程不能频繁的发消息给窗口,频繁的发消息给窗口,可能会造成窗口响应其他事件的缓慢,也是就让人感觉程序运行很慢;
9、要注意避免各种死锁、活锁发生,确实无法避免的话,就只能想法解锁,同时得注意解锁时引发的新的问题。
三、多线程的误区
1、使用处理事件()。非窗口的线程是没有窗口消息循环,而处理事件()命令是用于消息循环,因此在非窗口的线程上是不必加入“处理事件()”命令;
2、线程越多越好。线程并非越多越好,有些人将单线程改成多线程后,发现程序能处理更多的任务了,实际上这种方法是建立别的程序的痛苦之上(当然系统有空闲资源就并当别论了),别的程序可能因此而变慢。并且,线程数过多,会使CPU在线程间切换的开销增加,因而使速度变慢,降低系统性能。在一些阻塞式、耗资源少的线程上需要适当的增加线程数量,以免程序无响应。
四、许可区
1、许可区(一般称为临界区),不论是硬件许可资源,还是软件许可资源,多个线程必须互斥地对它进行访问,每个线程中访问许可资源的那段代码称为许可区。
2、注意事项:
①、如果有若干线程要求进入许可区,一次仅允许一个线程进入;
②、任何时候,处于许可区内的线程不可多于一个。如已有线程进入自己的许可区,则其它所有试图进入许可区的线程将被挂起,并一直持续到进入许可区的线程退出;
③、进入一个空闲的许可区时,耗时极少,但是进入一个需等待的许可区时,耗时相对较长,因此需要避免经常出现进入需等待的许可区;
④、创建后许可区,在不再使用时,需要将其删除;
⑤、在使用许可区时,应尽量减少许可区内代码,避免使用需长时间处理的代码,使进入许可区的线程能尽快退出,以便其它线程能进入许可区;
⑥、避免将整个线程处于许可区内,尽管它不会出错,但是由于后来要求进入许可区的线程全部会被挂起,也就会出现虽然是多线程,但实际是以单线程方式执行;
⑦、访问相同的许可资源时,必须是以相同的许可区进入访问,以不同的许可区进入访问将可能会使许可区变的无意义(我在这个坑里蹲了很久,郁闷啊!)。
3、许可区缺点
①、无法侦测某个许可区是否可进入。
五、线程同步
1、临界区(CriticalSection)
易语言中称为许可区,这种速度最快,但只能用于本进程的线程同步;
2、事件(Event)
事件可以跨进程使用,它有两种状态、两种类型:有信号状态和无信号状态、手动重置事件和自动重置事件。手动重置事件被设置为有信号状态后,会唤醒所有等待的线程,而且一直保持为有信号状态,直到程序重新把它设置为无信号状态。自动重置事件被设置为有信号状态后,会唤醒“一个”等待中的线程,然后自动恢复为无信号状态。
3、互斥器(Mutex)
互斥器的功能和临界区很相似,互斥器所花费的时间比临界区多的多,同时它可以跨进程使用。等待一个被锁住的互斥器可以设定超时退出,不会像临界区那样无法得知临界区的情况,而一直死等。
4、信号量(Semaphore)
与临界区相比,它信号量可以跨进程使用,可以设定同时进入资源总数。
六、线程通信
线程通信是一般都是需要配合线程同步来使用:
1、使用全局变量进行通信,推荐使用这种方法,该是最快、最方便的通信方式;
2、使用消息通信(需要有消息队列才能使用);
3、使用Socket进行通信(可以跨计算机使用);
附:更正一下:对象.发送信息()、对象.投递信息()多线程中也不是稳定的,应该使用SendMessage()、PostMessage()。感谢hesiyuanmm的反馈。
页:
[1]