linux线程锁的迷思

http://www.ibm.com/developerworks/cn/linux/thread/posix_thread3/#1

当多线程想要等待某一条件成立时,用pthread_cond_wait来阻塞线程,

  • 首先明确wait的作用,pthread的wait与lock都可以阻塞线程等待“条件”成立,区别在于:

wait之后的发送来的条件成立(pthread_cond_broadcast())才有效,之前发生都被丢弃了,也就是说wait的首次调用肯定会阻塞线程,不会因条件成立过而向下运行;

而lock的条件在成立(信号量未被别人lock )后会一直维持   unlock的状态,那么线程首次调用lock就不一定会阻塞,而取决于“条件”状态。

  • 那么问题来了,为什么pthread_cond_wait调用之前必须保证本线程已经被lock了?为什么wait要被设计成这样?

据说是因为wait时如果有条件达成,会因为wait未能及时加入到队列中而错失这个条件,只是这样吗?

试一下就知道了,本线程未被lock时调用wait也是可以的,wait也能正常工作啊。

下面用测试代码来说明这个问题:

void * foo1(void * p){
        while(1){
                printf("foo1 wait begin\n");
                pthread_cond_wait(&c,&m);
                printf("foo1 wait end\n");
sleep(1);
        }
}
void * foo2(void * p){
        while(1){
                printf("foo2 wait begin\n");
                pthread_cond_wait(&c,&m);
                printf("foo2 wait end\n");
sleep(1);
        }
}
void * foo3(void * p){
        while(1){
                getchar();
                pthread_cond_broadcast(&c);
        }
}
int pthreadTest(){
        pthread_t  tid[10];
        pthread_create(tid+2,NULL,foo1,NULL);
        pthread_create(tid+1,NULL,foo2,NULL);
        pthread_create(tid+3,NULL,foo3,NULL);
        pthread_join(*(tid+1),NULL);
        pthread_join(*(tid+2),NULL);
}

结果

foo2 wait end

foo2 wait begin

foo1 wait end

foo1 wait begin

foo2 wait end

foo2 wait begin

foo1 wait end

foo1 wait begin

从结果上看 wait唤醒的时候会lock住mutex,当线程循环一遍后再次调用wait而释放mutex时 再唤醒其他线程。

那么,线程间使用不同的mutex是否就可以同时唤醒两个线程的wait了呢,也就是是否就能够单纯的用cond控制wait唤醒?每个线程一个mutex,这样就不会被其他线程锁住自己的信号量了吧,下面测试一下:

void * foo1(void * p){
        while(1){
                printf("foo1 wait begin\n");
                pthread_cond_wait(&c,&m11111);
                printf("foo1 wait end\n");
sleep(1);
        }
}
void * foo2(void * p){
        while(1){
                printf("foo2 wait begin\n");
                pthread_cond_wait(&c,&m22222);
                printf("foo2 wait end\n");
sleep(1);
        }
}

结果:

foo2 wait end

foo2 wait begin

foo2 wait end

foo2 wait begin

这?????,只唤醒了一个线程(具体唤醒哪个线程取决于哪个线程先执行),pthread_cond_broadcast声称激活所有等待线程,但看来并不简单,可能是wait的时候同一个cond是事实上他的激活方式是串行的(也就是每次的pthread_cond_signal后根据wait的情况决定下一次是否signal),另一种可能是后运行的线程的mutex出现了问题。

那么将pthread_cond_broadcast改成pthread_cond_signal后测试看看,结果:

foo1 wait end

foo1 wait begin

foo2 wait end

foo2 wait begin

foo1 wait end

foo1 wait begin

foo2 wait end

foo2 wait begin

这是否说明了与mutex无关而激活方式确实是串行的呢?再试试每次pthread_cond_broadcast后调用unlock(&m11111),结果:

foo1 wait end

foo2 wait end

foo1 wait begin

foo2 wait begin

foo1 wait end

foo2 wait end

foo1 wait begin

foo2 wait begin

恩,看来应该是后运行的线程在wait唤醒时无法lock自己的mutex,但是根据刚才signal的结果看,这又是谁lock了它的mutex呢?

经过思考,终于把自己搞晕了......一个问题如果不能理解,那就会产生宗教,比如:“别问为什么,书上就是这么说的!”

所以   真正的原因就是:一个特定条件只能有一个互斥对象,而且条件变量应该表示互斥数据“内部”的一种特殊的条件更改。一个互斥对象可以用许多条件变量(例如,cond_empty、cond_full、cond_cleanup),但每个条件变量只能有一个互斥对象。

同时,wait也只有一个正确用法:如果线程正在等待某个特定条件发生,它应该如何处理这种情况?它可以重复对互斥对象锁定和解锁,每次都会检查共享数据结构,以查找某个值。但这是在浪费时间和资源,而且这种繁忙查询的效率非常低。解决这个问题的最佳方法是使用 pthread_cond_wait() 调用来等待特殊条件发生。

所以我们记住这个结论:照着他这么用就得了......否则只会走火入魔

时间: 2024-10-24 16:54:41

linux线程锁的迷思的相关文章

Linux 线程锁 pthread_mutex_t

1)初始化线程锁 静态初始化: pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; 或者动态初始化: int pthread_mutex_init(pthread_mutex_t*mutex,constpthread_mutexattr_t* attr); 其中 attr 用于指定互斥锁属性,如果为NULL则使用缺省属性. 函数成功执行后,互斥锁被初始化为未锁住态. 2)锁的操作 2.1)加锁: int pthread_mutex_lock(pt

linux 线程锁

1.互斥量:Mutex a.用于互斥访问 b.类型:pthread_mutex_t,必须被初始化为PTHREAD_MUTEX_INITIALIZER(用于静态分配的mutex,等价于 pthread_mutex_init(…, NULL))或者调用pthread_mutex_init.Mutex也应该用pthread_mutex_destroy来销毁. #i nclude <pthread.h> 1) int pthread_mutex_init( pthread_mutex_t *restr

Linux线程同步---互斥锁

线程中互斥锁使用的步骤与信号量相似! 1.首先定义互斥锁变量,并初始化 pthread_mutex_t mutex_lock;pthread_mutex_init(&mutex_lock,NULL);2.在操作前对互斥量进行加锁操作 pthread_mutex_lock(&mutex_lock);3.操作完毕后进行解锁操作 pthread_mutex_unlock(&mutex_lock); 所有操作均在加锁和解锁操作之间进行,保证同时仅仅对有一个操作对关键变量或是区域进行操作.

Linux 线程与进程,以及通信

http://blog.chinaunix.net/uid-25324849-id-3110075.html 部分转自:http://blog.chinaunix.net/uid-20620288-id-3025213.html 1.首先要明确进程和线程的含义: 进程(Process)是具有一定独立功能的程序关于某个数据集合上的一次运行活动,是系统进行资源分配和调度的一个独立单位.与程序相比,程序只是一组指令的有序集合,它本身没有任何运行的含义,只是一个静态实体.进程是程序在某个数据集上的执行,

Linux 线程管理

解析1 LINUX环境下多线程编程肯定会遇到需要条件变量的情况,此时必然要使用pthread_cond_wait()函数.但这个函数的执行过程比较难于理解. pthread_cond_wait()的工作流程如下(以MAN中的EXAMPLE为例):       Consider two shared variables x and y, protected by the mutex mut, and a condition vari-        able cond that is to be

Linux 线程模型的比较:LinuxThreads 与 NPTL

Linux 线程模型的比较:LinuxThreads 与 NPTL 本文参照来源:IBM开发者论坛 前奏:关于POSIX 可移植操作系统接口(英语:Portable Operating System Interface,缩写为POSIX),是IEEE为要在各种UNIX操作系统上运行的软件,而定义API的一系列互相关联的标准的总称,其正式称呼为IEEE Std 1003,而国际标准名称为ISO/IEC 9945.此标准源于一个大约开始于1985年的项目.POSIX这个名称是由理查德·斯托曼应IEE

取证分析的迷思

由于证物特性的不同,在进行digital evidence的取证分析时,第一要务便是确保电子证据在过程中不致遭受污染或破坏.且由于是和计算机科技有关,随着科技的进步也会多所变化,因此取证分析也要能跟的上变化. 大家耳熟能详的就不提了,在此想分享的是取证分析的从业工作者在取证分析上的迷思,给大家参考.以避免犯了相关病征而不自知. 1.只知操作工具,而未能了解原理或本质 只知使用工具,而未能了解何以如此,那就可能成了"取证分析匠",在不懂"为何"及"如何&qu

一个简单的线程锁------pthread和win32的临界区(Critical Section)

临界区: 临界区是指一个小代码段,在代码能够执行前,它必须独占对某些资源的访问权.这是让若干代码能够"以原子操作方式"来使用资源的一种方法. 所谓原子(atomic)操作方式,是指这段代码知道没有别的线程要访问这个资源. 说明: 1.  MacOSX,Windows有自己的线程模型, pthread可以说是跨平台的线程编程模型解决方案,当然对pthread不熟悉的也可以使用本地线程模型, 其实pthread的win32版本也是基于本地线程模型的, pthread-win32的mutex

Linux互斥锁、条件变量和信号量

Linux互斥锁.条件变量和信号量  来自http://kongweile.iteye.com/blog/1155490 博客分类: Linux sem_init:初始化信号量sem_t,初始化的时候可以指定信号量的初始值,以及是否可以在多进程间共享.sem_wait:一直阻塞等待直到信号量>0.sem_timedwait:阻塞等待若干时间直到信号量>0.sem_post:使信号量加1.sem_destroy:释放信号量.和sem_init对应. 进行多线程编程,最应该注意的就是那些共享的数据