Posted on 2008-01-10 04:02
Fox 阅读(2707)
评论(10) 编辑 收藏 引用 所属分类:
G游戏编程
Author: Fox
一、多线程安全的引入:
关于什么是多线程、为什么使用多线程的问题,大家可以看看Jim Beveridge & Robert Wiener的《Win32多线程程序设计》(侯捷 译),或者其他随便一本提到多线程的书或文章。这里只是提到Windows环境下多线程容易引发的问题和解决办法。
1、线程在时间片结束时退出做不到
由于Windows属于分时操作系统,系统会为每个线程分配响应的时间片使其工作,绝大多数线程不可能在时间片结束的时候完成其工作,而下一个时间片就有可能分配给其他线程。
2、线程独立做不到
如果线程间不存在依赖关系,即线程A的执行不依赖于线程B的执行,此时即使线程B被打断,由于线程独立,所以二者也可以相安无事。
然而,在多线程解决方案中,线程间的通信是频繁而且必要的。线程通信主要有两种情况:
1) 多个线程共享相同资源;
2) 一个线程的执行依赖于其他线程的结果或执行情况。
这时,我们就需要实现共享资源及线程执行的同步。
二、多线程安全的解决方案:
因此,多线程安全的目标就是实现共享资源的互斥访问和线程执行的同步通信。
通过对操作系统的学习,我们知道线程同步主要有以下方法:
1) 临界段(Critical Section)
a) 临界资源的取舍,宜少不宜多,宜短不宜长,一个线程只能最多等待一个临界段;
b) 无法侦测一个临界段是否已经被放弃;
c) 临界段属于用户对象。
2) 互斥锁(Mutex)
同临界段一样,互斥锁也主要用于保证资源的原子访问,二者的不同之处在于:
a) 互斥锁属于可具名内核对象;
b) 互斥锁可以跨进程使用,临界段只能用于同一进程内;
c) 互斥锁可以指定等待时间,而且可以等待其他内核对象。
3) 事件(Event)
a) 事件重置具有人工重置和自动重置两种方式,简单说来,二者分别用于多读和单写;
b) 事件主要用于线程间相互通知(唤醒);
C) 事件属于可具名内核对象。
4) 信号量(Semaphore)
a) 信号量属于可具名内核对象;
b) 信号量没有拥有者,可被任一线程释放;
关于Win32中这四种对象的使用和要点,更详细的介绍可以参照《Win32多线程程序设计》或《Windows核心编程》(Jeffrey Richter)等。
三、多线程安全的实现:
将对数据(对象、模型、消息、Socket)的I/O处理放在同一个I/O线程中,保证如队列的push/pop操作、链表的insert/delete操作、文件的write操作、socket的recv/send操作、全局变量的write操作等的互斥访问。
新建独立模块,尤其是使用第三方库的独立模块,大多会创建独立的新线程。此时就需要对新线程中的数据操作加以注意,可以通过对操作数据的加锁访问解决同步问题,当然,更常见的处理方式是将新线程中的数据操作发送到专门的I/O线程中处理。
总之,多线程安全是个常说常新的话题,现在有人提出Lock-Free数据结构的解决方案(Maged M. Michael),也有所谓的Wait-Free的解决方案(Maurice Herlihy),而国内网游界的大牛云风同学更是提出了单线程多进程的观点和解决方案(因为不了解,按字面有可能存在断章取义之嫌)。但不管怎么样,从中至少可以看出,多线程,说来话长。
零零散散、东拉西扯、不知所云的讲了一些东西,未必正确,更不能当作知识。全当是对上次的承诺有个交代。
/*****************************************************************************
想把多线程的问题搞明白,不是说看看操作系统教材,写点多线程读写的代码就够的。且不论孰是孰非,
单就网上诸多高手新学对加锁策略铺天盖地的争执说辞甚至相互批判指责,足可见多线程开发并非只言
片语即可挑明。
为防止陷入细节争论,这里先作声明:小文仅就所学略抒拙见,无意引起争端……
*****************************************************************************/