unix环境高级编程之线程篇(二)

本片文章着重讲解线程同步,但是由于时间仓促,例子还是欠缺,会在下次补上。


四、线程同步
1、互斥量
互斥量(mutex)本质是一般锁,在访问共享资源前对互斥量进行加锁,在访问完成后释放互斥量的锁。对互斥量进行加锁以后,任何尝试再次对互斥量加锁的线程将会被阻塞直到当前线程释放该互斥锁。如果释放互斥锁时有多个线程阻塞,所有在该互斥锁上的阻塞线程都会变成可运行状态,第一个变为运行状态的线程对互斥量加锁,其他线程将会看到互斥量再次被锁住,只能继续等待。互斥量用pthread_mutex_t数据类型来表示,在使用互斥变量以前,都需要初始化,初始化可采用静态方式,直接将互斥量
值为PTHREAD_MUTEX_INITIALIZER,也可以采用动态方式,但是动态方式需要调用两个函数来完成:
#include
int pthread_mutex_init(pthread_mutex *mutex,const pthread_mutexattr_t *attr);//动态申请
int pthread_mutex_destroy(pthread_mutex_t *mutex);//销毁
若成功返回0,失败返回错误编号
要默认的属性初始化互斥量,只需把attr设置为NULL即可。

加锁和释放锁可用下面三个函数:
#include
int pthread_mutex_lock(pthread_mutex_t *mutex);//加锁,如果互斥量已经加锁则一直阻塞,否则锁住返回0
int pthread_mutex_trylock(pthread_mutex_t *mutex);//加锁,如果互斥量已经加锁则返回EBUSY,不阻塞,否则锁住返回0
int pthread_mutex_unlock(pthread_mutex_t *mutex);//解锁
//若成功则返回0,失败返回错误编号


2、死锁
如果一个线程尝试对同一个互斥量加锁两次,那么它自身就会陷入死锁状态,使用互斥量一定要注意不能让线程处于死锁状态死锁的四个必要条件:
1、互斥条件;2、不可剥夺;3、循环等待;4、请求和保持

3、读写锁
读写锁与互斥量类似,不过读写锁允许更高的并发性。互斥量要么锁住要么释放,而且一次只有一个线程对它加锁。读写锁有三种状态:读模式下加锁状态,写模式下加锁状态,不加锁状态。一次只有一个线程可以占有写模式的读写锁,但是多个线程可以同时拥有读模式的读写锁。
当读写锁是写加锁状态时,在这个锁释放前,所有尝试对这个锁加锁的线程都会被阻塞;
当读写锁是读加锁状态时,所有试图以读模式对它进行加锁的线程都可以得到访问资源,但是此时如果线程希望用写模式对此锁进行加锁时,则会一直阻塞直到所有线程都释放了读锁;当读写锁是读加锁状态时,有其他线程尝试对写模式加锁,读写锁通常会阻塞随后的读模式的加锁请求。这样做的目的是避免读模式锁被长期占用,而等待的写模式锁请求一直得不到满足。
读写锁非常适用于对数据结构读的次数大于写的情况。
读写锁也叫共享-独占锁,当读写锁以读模式加锁时,他是以共享模式锁住的;当他以写模式锁住时,它是以独占模式锁住的。
读写锁使用pthread_rwlock_t数据类型表示,可以使用静态方式初始化,即设置为:PTHREAD_RWLOCK_INITIALIZER;或者使用动态方式
#include
int pthread_rwlock_init(pthread_rwlock_t *rwlock,const pthread_rwlockattr_t attr);
int pthread_rwlock_destroy(pthread_rwlock_t *rwlock);
如果需要以默认属性初始化读写锁,attr可值为NULL;返回值成功都是0,出错返回错误编号


加锁和解锁使用以下API:
#include
int pthread_rwlock_rdlock(pthread_rwlock_t *rwlock);
int pthread_rwlock_wrlock(pthread_rwlock_t *rwlock);
int pthread_rwlock_unlock(pthread_rwlock_t *rwlock);
前两个加锁函数都是阻塞函数,在不能锁住锁得时候就会被阻塞,返回值成功都是0,出错返回错误编号

linux还提供了非阻塞的方式对锁进行加锁
#include
int pthread_rwlock_tryrdlock(pthread_rwlock_t *rwlock);
int pthread_rwlock_trywrlock(pthread_rwlock_t *rwlock);
这两个函数不阻塞,可以加锁时返回0,如果锁已经锁住会返回EBUSY,出错返回错误编号。


4、条件变量
条件变量是另一种线程同步机制,条件变量给多个线程一个回合的场所,条件变量与互斥量一同使用时,允许多个线程以无竞争的方式等待特定的条件发生。条件本身是由互斥量保护的。线程在改变条件状态前必须首先锁住互斥量,其他线程在获得互斥量之前不会察觉到这种改变因为必须锁住互斥量以后才能计算条件。

条件变量用pthread_cond_t数据类型来表示,初始化方式有静态:值为PTHREAD_COND_INITIALIZER,或者动态方式
#include
int pthread_cond_init(pthread_cond_t *cond,const pthread_condaddt_t *attr);
int pthread_cond_destroy(pthread_cond_t *cond);//两者的返回值都是成功返回0,错误返回错误编号。

使用pthread_cond_wait等待条件变为真,如果在给定的时间内条件不能满足,那么会生成一个出错码的返回值。
#include
int pthread_cond_wait(pthread_cond_t *cond,pthread_mutex_t *mutex);
int pthread_cond_timedwait(pthread_cond_t *cond,pthread_mutex_t *mutex,const struct timespec *timeout);


传递给pthread_cond_wait的互斥量对条件加以保护,调用者把锁住的互斥量传给函数,函数调用把线程放到等待条件的线程列表上,然后对互斥量解锁,这两个操作是原子操作。pthread_cond_wait返回时,互斥量再次被锁住。
其实流程就是:互斥量加锁-->函数调用-->互斥量解锁-->等待条件变为真-->其他线程获得锁,并加锁-->条件改变,接收信号-->等待释放互斥量-->互斥量加锁成功,判断条件成立-->函数返回,向下执行。
pthread_cond_timedwait的工作方式类似,只是多了一个timeout,这个参数指定了等待的时间,是一个绝对时间,如果时间到了条件还没有出现,pthread_cond_timedwait将重新获取互斥量然后返回错误WTIMEOUT。这两个函数调用成功返回时,线程需要重新计算条件,因为其他线程可能已经在运行并改变了条件。
有两个函数用于通知线程条件已经满足。pthread_cond_signal函数将唤醒等待该条件的某个线程,而pthread_cond_broadcast函数将唤醒等待该条件的所有线程。
#incldue
int pthread_cond_signal(pthread_cond_t *cond);
int pthread_cond_broadcast(pthread_cond_t *cond);
调用这两个函数也称为向线程或条件发送信号。必须注意一定要在改变条件状态以后再给线程发送信号。

你可能感兴趣的:(原创,pthread,Linux)