之前我们了解了在AS3中线程之间的同步该如何实现,那么现在我们来看看同步可能会导致的问题。同步技术保证了多个线程对同一个对象进行操作时的安全性之外,带来了一个新的问题,那就是死锁的问题。
什么是死锁?
百度一下,我们得到一个大致的介绍:所谓死锁是指两个或两个以上的进程在执行过程中,因争夺资源而造成的一种互相等待的现象,若无外力作用,它们都将无法推进下去。此时称系统处于死锁状态或系统产生了死锁,这些永远在互相等待的进程称为死锁进程。 由于资源占用是互斥的,当某个进程提出申请资源后,使得有关进程在无外力协助下,永远分配不到必需的资源而无法继续运行,这就产生了一种特殊现象:死锁。
那么AS3中该怎么样来模拟一个死锁呢?这里对我来说确实是花了一点时间,不过最后定下的Demo思路还比较简单:
我们创建一个DeadLockDemo项目,其文档类DeadLockDemo.as作为主线程,同时创建一个子线程MyWorker.as;我们在主线程中创建两个实现同步需要的互斥对象_mutex1和_mutex2;然后我们的主线程创建MyWorker子线程并运行,侦听子线程的开始运行事件,保证子线程开始运行时调用主线程的方法func,同时子线程内部获取主线程创建的两个互斥对象,然后调用子线程的func方法;
现在我们知道主线程和子线程中都有名为func的方法,且这两个func方法应该是同时运行(实际上有可能主线程先执行,也有可能子线程先执行,CPU调度无法控制),接下来我们的主线程func方法如下:
1 private function func():void 2 { 3 trace("进入主线程的func方法!"); 4 //锁定 _mutex1 对象 5 _mutex1.lock(); 6 try 7 { 8 //巨大的运算量保证子线程的代码执行在这段时间内开始 9 largeComputationalCost(); 10 //由于子线程开始运行后就锁定了 _mutex2 对象, 所以这里会被阻塞 11 _mutex2.lock(); 12 try 13 { 14 trace("主线程的func方法运算!"); 15 } 16 finally 17 { 18 _mutex2.unlock(); 19 } 20 } 21 finally 22 { 23 trace("退出主线程的func方法!"); 24 _mutex1.unlock(); 25 } 26 }
largeComputationalCost方法就不说了,上一篇中有,子线程中的func方法如下:
1 private function func():void 2 { 3 trace("进入子线程的func方法!"); 4 //锁定 _mutex2 对象 5 _mutex2.lock(); 6 try 7 { 8 //巨大的运算量保证主线程的代码执行在这段时间内开始 9 largeComputationalCost(); 10 //由于主线程开始运行后就锁定了 _mutex1 对象, 所以这里会被阻塞 11 _mutex1.lock(); 12 try 13 { 14 trace("子线程的func方法运算!"); 15 } 16 finally 17 { 18 _mutex1.unlock(); 19 } 20 } 21 finally 22 { 23 trace("退出子线程的func方法!"); 24 _mutex2.unlock(); 25 } 26 }
下面我们走一遍逻辑看看:如果主线程的func先运行,_mutex1对象被锁定;largeComputationalCost方法被执行,由于这个方法运算量太大,所以在主线程的func中largeComputationalCost方法运行结束前子线程的func会开始运行;此时_mutex2对象被锁定,子线程的func中largeComputationalCost方法运行;主线程的largeComputationalCost方法运行结束后会申请锁定_mutex2对象,但是_mutex2对象已经锁定,所以这里会停止执行直到_mutex2对象解锁才继续运行;子线程的func中largeComputationalCost方法运行结束后会申请锁定_mutex1对象,但是_mutex1对象已经锁定,所以这里会停止执行直到_mutex1对象解锁才继续运行;
好吧,看出来没,两个方法都在等待互斥对象的解锁,但是主线程等待的_mutex2对象必须等到子线程的func方法运行结束时才会解锁,而子线程等待的_mutex1对象必须等到主线程的func方法运行结束时才会解锁,主线程等待子线程的同时子线程也在等待主线程,但是双方都不会继续执行,陷入了死锁中;
运行DeadLockDemo项目看看,会得到下面的输出:
1 进入子线程的func方法! 2 进入主线程的func方法! 3 巨大的运算耗时: 907 毫秒 4 巨大的运算耗时: 906 毫秒
等待15秒后继续输出:
1 退出主线程的func方法!Error: Error #1502: 脚本的执行时间已经超过了 15 秒的默认超时设置。 2 at flash.concurrent::Mutex/lock() 3 at DeadLockDemo/func()[E:\flex4.7\DeadLockDemo\src\DeadLockDemo.as:54] 4 at DeadLockDemo/workerStateHandler()[E:\flex4.7\DeadLockDemo\src\DeadLockDemo.as:40] 5 6 子线程的func方法运算! 7 退出子线程的func方法!
由于死锁导致程序暂停;而Flash有15秒的默认超时时间,一段脚本的运行时间超出15秒就会报错并终止这段脚本的执行;以前一般是for循环写成死循环才会出现该错误,现在还有一种可能,就是多线程的死锁。
其实对于AS3来说,用到多线程的地方本来就不多,向Demo中的情况就更少了,所以大家做个了解就行了。
下一篇我们认识一下flash.concurrent包中的另外一个类——Condition。
源码下载: