三. 如何避免多线程出现争夺资源问题?
多线程执行是WinCE系统的特点之一,我们在编程时应该要大力的利用,以提高程序的执行效率。但是也必须要小心处理多线程问题,避免出现多线程争夺资源。一般来说,针对不同的情况有不同的处理方式,主要分为以下两种情况:
(1)建立同步机制,避免进程互斥竞争
在正常情况下,一个进程的运行一般是不会影响到其它正在运行的进程的。但是对于某些有特殊要求的,如需要以独占资源的进程(或线程)就要求在其进程运行期间不允许其它进程试图使用此资源,这就引出了进程互斥竞争的问题。要实现进程互斥的核心思想比较简单:进程在启动时首先检查当前系统是否已经存在有此进程的实例,如果没有,进程将成功创建并设置标识实例已经存在的标记。此后再创建进程时将会通过该标记而知晓其实例已经存在,从而保证进程在系统中只能存在一个实例。具体可以采取内存映像文件、有名事件量、有名互斥量以及全局共享变量等多种方法来实现。
一般来说,使用临界区、互斥、信号量和事件可解决线程同步的问题。这里介绍两种最常用的方法:①临界区:它是一种最直接的线程同步方式。所谓临界区,就是一次只能由一个线程来执行的一段代码。如把初始化数组的代码放在临界区内,另一个线程在第一个线程处理完之前是不会被执行的。②互斥机制:互斥非常类似于临界区,但有两个关键的区别:首先,互斥可用于跨进程的线程同步。其次,互斥能被赋予一个字符串名字,并且能通过引用此名字创建现有互斥对象的附加句柄。
另外,还需要提醒的是临界区与事件对象的最大的区别是在性能上。临界区在没有线程冲突时,一般要用到10-15个时间片,而事件对象由于涉及到系统内核则要到用400-600时间片。所以,临界区是一个进程里的所有线程同步的最好办法,因为它不是系统级的,只是进程级的。
(2)建立资源分配图,避免进程死锁
一般来说,多线程产生死锁是与进程对资源的需求、进程的执行速度、资源的分配策略有关。因此,如果无法通过设计来避免死锁,则应该建立资源分配图。通过仔细跟踪系统中的所有线程和它们锁定的共享资源,周期性地进行检查和维护资源分配图,及时发现循环等待的特征,从而事先识别潜在死锁。
建立资源分配图需要识别每个受保护的共享资源,以及指向其中某一资源的所有线程。一般可以采用这几个步骤来实现:①识别所有可能阻塞的系统调用,因为每个受保护的共享资源总是有一些与访问它有关的阻塞调用。②识别出获取共享资源的阻塞调用之后,在源代码中查找它们的各次调用情况。③对于每次调用,记录下指向资源的线程名称和该资源的名称。通常调用本身将受保护的资源作为一个参数来传递,通过这种方式可以识别出所有受保护的资源以及分配资源的线程。④建立资源分配图,并检查是否有任何资源存在循环路径。
当然,如果预先确定每一个共享资源并建立分配图是不实际或不可能的,此时可以增加一些额外的代码,以便在系统运行时检测出潜在的死锁。例如目前有许多不同的算法都致力于优化这个检测过程,但本质上它们还是动态地建立某种资源分配图。