Kermit’s Gossip ? Blog Archive ? 对线程的思考之一(C语言)

来源:百度文库 编辑:神马文学网 时间:2024/04/25 16:39:34
昨天经老大的指点,学到了不少东西,也引发了我对线程编程的一些思考。
先下面引用一下老大的留言:)
“我认为嵌入式开发真正的难度在于,嵌入式开发,往往都是多线程的,把哪些任务归为哪个线程,线程之间的数据关系,那些数据是需要加锁保护的,在这种情况下,还要保证系统的稳定、避免竞争条件、还有性能要求,这样写程序简直是戴着枷锁跳舞。”
的确,处理竞争条件和死锁等问题,绝对是线程中的重中之重,太谨慎了,效率得不到提高,这样的话用线程还不如不用,得不偿失;相反,太“大气”了就会在程序中埋下死锁的伏笔,失去可靠性。
本文的想法就来试着用面向对象的设计思想,来解决老大上面提到的混乱的地方之一——死锁吧。并且在此设计基础上,为其他如数据关系,线程设计等问题做一点归纳。
先看看POSIX.1和GTK+的创建线程的接口(BTW: M$ Win的线程接口太丑陋了)。先看下面两个创建线程的函数:
POSIX.1:
int pthread_create(pthread_t *restrict    tidp,
const pthread_attr_t *restrict attr,
void *(*start_rtn) (void*),void *restrict arg);
GTK+:
GThread* g_thread_create(GThreadFunc func,
gpointer data,
gboolean joinable,
GError **error);
实际上POSIX.1和GTK+的对象设计思想是一样的,你都可以把一个线程看作一个对象,不同之处仅仅在于控制对象的句柄(handle)不一样:POSIX.1是个文件描述符,而GTK+则是个指针。顺便说一句,在Qt的抽象中,线程的句柄才是个不折不扣的线程对象。如果把Thread看作一个类,那么上述两个函数都可谓是Thread类的构造函数。我们看到,线程将要执行的操作被当作一个函数指针传入“构造函数”中,对于这种设计思想,我要强调一点我的看法:
对于上面的抽象,线程所执行的任务,被看作是线程的一个属性,而不是方法。
属性和方法有一个最要的区别:属性是对象的实体组成部分,而方法则是对象的行为准则。区别两者的最简单的方法,就是看他们是否可以通过赋值得到。很显然,上面的func是运行create()时通过赋值而获得的,POSIX也一样。
可参见GTK+对Gthread的实现代码:
typedef struct _GThread         GThread;
struct  _GThread
{
/*< private >*/
GThreadFunc func;
gpointer data;
gboolean joinable;
GThreadPriority priority;
};
把这个搞清楚,那么我们就应当能够意识到,既然是属性,就有自己的类型,也就可以有自己的数据和方法!这就是说,func是一个对象,它可以拥有自己的方法。基于这种思想,我接下来就把锁Mutex设计成func的属性。
光说无用,看看这个例子吧:假设有T1,T2,T4,T4四个线程,要按照如下方式用到三个资源R1,R2,R3,且只有当所有使用都完成后,才会解锁:
T1:R1-R2
T2:R2-R1
T3:R3-R1-R2
T4:R3-R2
很明显,这里会产生死锁。那么,如何处理呢?在处理死锁的诸多方法中,最简单,也是最有效的方法,就是把资源“顺序化”,这个原理是操作系统的基础,我就不再赘述了。
我主要谈谈我对此处设计的看法。我的建议是把所有资源所对应的锁Mutex抽象出来,构造一个类(记为ARMutex,意思是All Resources Mutex),用这个类来控制各种锁的情况。对于这个例子,可以这样写代码:
typedef struct{
GMutex *R1;
GMutex *R2;
GMutex *R3;
}ARMutex;
首先,我们把所有线程执行中使用资源的顺序罗列出来:
T1:R1-R2
T2:R2-R1
T3:R3-R1-R2
T4:R3-R2
在所有情况中寻找被以不同顺序使用的相同的资源组:这里只有一个R1-R2。因此,你必须在编译前对R1-R2进行顺序化,至于是R1-R2还是R2-R1,应当根据线程的优先级等属性进行选择,这也是优化的一部分。 然后,在QRMutex中实现如下方法:
void armutex_lock(GMutex *mutex) {
if(mutex == R1 || mutex == R2) {
g_mutex_lock(R1);
g_mutex_lock(R2);
}else {
g_mutex_lock(mutex);
}
}
void armutex_unlock(GMutex *mutex) {
if(mutex == &R1 || mutex == &R2) {
g_mutex_unlock(&R1);
g_mutex_unlock(&R2);
}else {
g_mutex_unlock(mutex);
}
}
很显然,以后要对所有资源锁进行操作,就不能直接用g_mutex_unlock(mutex)了,应该用armutex_lock()和armutex_unlock();如果你觉得函数代价比较大,当然你可以还用宏进行优化。
这样一来,在以后的编程过程中,就不必在每次都考虑会不会死锁,直接用armutex_lock加锁就行。当程序过程中情况发生改变时(如有第5个线程的时候),不要考虑每个线程的具体任务是什么,你就相信使用armutex_lock()是安全的,不会发生死锁,先把你的代码全部写好,然后在从代码中提出对资源的使用顺序,加入到上面的表中进行分析。最后,根据分析的结构修改armutex_lock()中的代码即可。
最后,我们再来看看如何设计自己的操作“类”,就是传入线程的那个函数指针的类型。注意,它是一个“类”,在这里应将其看作是一个函数对象,ARMutex就是它的一个属性,我们自己的线程任务,其实应该被设计为从一个基础类继承而来的子类,在线程接受MyThreadFunc_n作为参数时,我们可以想象,这时候传入的参数是一个函数对象——看下图:

实际编写代码的过程中,MyThreadFunc甚至可以不用写出来,只要知道有这么个抽象层次就行,我们并不会在这里用到多态,只是想让每一个MyThreadFunc_n使用同一个静态ARMutex变量而已。其中MyThreadFunc_n就是相应的线程Tn所要执行的具体代码(我上面忘记画出对应上例中线程T4的MyThreadFunc_4了,特此说明)。
Mutex应该是线程竞争中最简单,也是最常见的竞争情况了。涉及到同步的情况也不止Mutex一种,但是几乎所有的情况都可以套用这种格式。这篇文章的主要目的也就是要理清POSIX.1和GTK+之类的C库对Thread的抽象思想,在此思想上寻求更好的解决方案。
后面一篇,我还会结合QThread来谈谈对线程的另一种OO处理方式。
最后要说一下,我对GTK+不是很熟悉,也不是很喜欢C的风格,所以如果什么地方有错误,还望在此指出,谢谢!
Kermit’s Gossip ? Blog Archive ? 对线程的思考之一(C语言) Kermit’s Gossip ? Blog Archive ? 对嵌入式开发的一点思考 Kermit’s Gossip xiuli’blog ? Blog Archive ? 对个人知识管理的思考(1):知识管... xiuli’blog ? Blog Archive ? 对个人知识管理的思考(2):知识管... xiuli’blog ? Blog Archive ? 对个人知识管理的思考(3):发展人... xiuli’blog ? Blog Archive ? 对个人知识管理的思考(4):我们有... xiuli’blog ? Blog Archive ? 对个人知识管理的思考(5):做前学... xiuli’blog ? Blog Archive ? 对个人知识管理的思考(6):向同行... Fwolf’s Blog Blog Archive subversion和module_rewrite的小冲突? 油茶研究会 Blog Archive 气温对心理的影响 pearlher ? Blog Archive ? 三聚氰胺对人体的危害 C语言之可变参数问题 - 欢迎阁下光临我的网络日志-safeking‘s blog - 博... C语言之内存使用 - 欢迎阁下光临我的网络日志-safeking‘s blog - 博客园 Fwolf’s Blog Blog Archive Subversion的Repository列表完美解决方案 Yee‘s Blog » Blog Archive » 我在Yo2Blog上启用的插件 Ren-Huang‘s Blog | [C ]Visual C 的.NET Socket Matrix67: My Blog ? Blog Archive ? 数学无处不在:语言、... C 语言之一 内存使用 Fiancee’s Visa Blog Blog Archive Third Guil... Fwolf’s Blog Blog Archive 通过RSS订阅、邮件转发自动同步多个Blog(补遗) 钱涂无量Qiantu’s Weblog Blog Archive ? WordPress的架设指南 Cero’s Weblog Blog Archive ? web2.0中流行的设计元素:颜色 Cero’s Weblog Blog Archive web2.0中流行的设计元素:颜色