参考:《Linux c与c++一线开发实践》朱文伟 李建英
将书上代码敲了一下,存在这里以便后续开发参考
一、互斥锁
定义互斥锁pthread_mutex_t mutex;
互斥锁初始化(动态方式)int pthread_mutex_init(pthread_mutex_t *restrict mutex,const pthread_mutexattr_t *restrict attr);
关键字restrict只用于限定指针,用于告知编译器所有修改该指针所指向的内容的操作全部都是基于该指针的,即不存在其他进行修改操作的途径,这样的后果是帮助编译器进行更好的代码优化
互斥锁初始化(常量方式)
#define PTHREAD_MUTEX_INITIALIZER \
{ { 0, 0, 0, 0, 0, {0} } }
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
互斥锁上锁函数int pthread_mutex_lock(pthread_mutex_t *mutex);
成功返回0,否则返回错误码,如果调用时互斥锁已被上锁,则调用该函数的线程将阻塞。
互斥锁上锁函数int pthread_mutex_trylock(pthread_mutex_t *mutex);
成功返回0,否则返回错误码,如果调用时互斥锁已被上锁,函数立即返回EBUSY。
互斥锁解锁命令int pthread_mutex_unlock(pthread_mutex_t *mutex);
成功返回0,否则返回错误码,需要与上锁函数成对使用。
互斥锁的销毁int pthread_mutex_destroy(pthread_mutex_t *mutex);
成功返回0,否则返回错误码。
1.利用互斥锁的多线程累加
#include <stdio.h>
#include <unistd.h>
#include <pthread.h>
#include <sys/time.h>
#include <string.h>
#include <cstdlib>
int gcn = 0;
pthread_mutex_t mutex;
void *thread_1(void *arg){
int j;
for(j=0;j<10000000;++j){
pthread_mutex_lock(&mutex);
gcn++;
pthread_mutex_unlock(&mutex);
}
pthread_exit((void *)0);
}
void *thread_2(void *arg){
int j;
for(j=0;j<10000000;++j){
pthread_mutex_lock(&mutex);
gcn++;
pthread_mutex_unlock(&mutex);
}
pthread_exit((void *)0);
}
int main(){
int j,err;
pthread_t th1,th2;
pthread_mutex_init(&mutex,NULL);
for(j=0;j<10;j++){
err = pthread_create(&th1,NULL,thread_1,NULL);
if(err != 0){
printf("create new thread error:%s\n",strerror(err));
exit(0);
}
err = pthread_create(&th2,NULL,thread_2,NULL);
if(err != 0){
printf("create new thread error:%s\n",strerror(err));
exit(0);
}
err = pthread_join(th1,NULL);
if(err != 0){
printf("wait thread done error:%s\n",strerror(err));
exit(1);
}
err = pthread_join(th2,NULL);
if(err != 0){
printf("wait thread done error:%s\n",strerror(err));
exit(1);
}
printf("gcn=%d\n",gcn);
gcn = 0;
}
pthread_mutex_destroy(&mutex);
}
二、读写锁
多个线程可以同时读,不能同时写,并行性更好。
定义读写锁pthread_rwlock_t rwlock;
读写锁常量初始化pthread_rwlock_t rwlock = PTHREAD_RWLOCK_INITIALIZER;
读写锁动态初始化int pthread_rwlock_init(pthread_rwlock_t *restrict rwlock,const pthread_rwlockattr_t *restrict attr);
读模式下上锁
int pthread_rwlock_rdlock(pthread_rwlock_t *rwlock);
int pthread_rwlock_tryrdlock(pthread_rwlock_t *rwlock);
写模式下上锁
int pthread_rwlock_wrlock(pthread_rwlock_t *rwlock);
int pthread_rwlock_trywrlock(pthread_rwlock_t *rwlock);
读写锁解锁
int pthread_rwlock_unlock(pthread_rwlock_t *rwlock);
读写锁销毁
int pthread_rwlock_destroy(pthread_rwlock_t *rwlock);
1.互斥锁与读写锁速度对比
#include <stdio.h>
#include <unistd.h>
#include <pthread.h>
#include <sys/time.h>
#include <string.h>
#include <cstdlib>
int gcn = 0;
pthread_mutex_t mutex;
pthread_rwlock_t rwlock;
void *thread_1(void *arg){
int j;
volatile int a;
for(j=0;j<10000000;++j){
pthread_mutex_lock(&mutex);
a = gcn;
pthread_mutex_unlock(&mutex);
}
pthread_exit((void *)0);
}
void *thread_2(void *arg){
int j;
volatile int b;
for(j=0;j<10000000;++j){
pthread_mutex_lock(&mutex);
b = gcn;
pthread_mutex_unlock(&mutex);
}
pthread_exit((void *)0);
}
void *thread_3(void *arg){
int j;
volatile int a;
for(j=0;j<10000000;++j){
pthread_rwlock_rdlock(&rwlock);
a = gcn;
pthread_rwlock_unlock(&rwlock);
}
pthread_exit((void *)0);
}
void *thread_4(void *arg){
int j;
volatile int b;
for(j=0;j<10000000;++j){
pthread_rwlock_rdlock(&rwlock);
b = gcn;
pthread_rwlock_unlock(&rwlock);
}
pthread_exit((void *)0);
}
int mutextVer(void){
int j,err;
pthread_t th1,th2;
struct timeval start;
clock_t t1,t2;
struct timeval end;
pthread_mutex_init(&mutex,NULL);
gettimeofday(&start,NULL);
err = pthread_create(&th1,NULL,thread_1,NULL);
if(err != 0){
printf("create new thread error:%s\n",strerror(err));
exit(0);
}
err = pthread_create(&th2,NULL,thread_2,NULL);
if(err != 0){
printf("create new thread error:%s\n",strerror(err));
exit(0);
}
err = pthread_join(th1,NULL);
if(err != 0){
printf("wait thread done error:%s\n",strerror(err));
exit(1);
}
err = pthread_join(th2,NULL);
if(err != 0){
printf("wait thread done error:%s\n",strerror(err));
exit(1);
}
gettimeofday(&end,NULL);
pthread_mutex_destroy(&mutex);
long long total_time = (end.tv_sec - start.tv_sec) * 1000000 +
(end.tv_usec - start.tv_usec);
total_time /= 1000;
printf("total mutex time is %lld ms\n",total_time);
return 0;
}
int rdlockVer(void){
int j,err;
pthread_t th1,th2;
struct timeval start;
clock_t t1,t2;
struct timeval end;
pthread_rwlock_init(&rwlock,NULL);
gettimeofday(&start,NULL);
err = pthread_create(&th1,NULL,thread_3,NULL);
if(err != 0){
printf("create new thread error:%s\n",strerror(err));
exit(0);
}
err = pthread_create(&th2,NULL,thread_4,NULL);
if(err != 0){
printf("create new thread error:%s\n",strerror(err));
exit(0);
}
err = pthread_join(th1,NULL);
if(err != 0){
printf("wait thread done error:%s\n",strerror(err));
exit(1);
}
err = pthread_join(th2,NULL);
if(err != 0){
printf("wait thread done error:%s\n",strerror(err));
exit(1);
}
gettimeofday(&end,NULL);
pthread_rwlock_destroy(&rwlock);
long long total_time = (end.tv_sec - start.tv_sec) * 1000000 +
(end.tv_usec - start.tv_usec);
total_time /= 1000;
printf("total rwlock time is %lld ms\n",total_time);
return 0;
}
int main(){
mutextVer();
rdlockVer();
return 0;
}
结果显示,互斥锁比读写锁稍快,但从理论上读写锁更适合并发性要求高的地方。
三、条件变量
1.生产者 - 消费者问题
pthread_mutex_t mutex; //互斥锁,用于生产线程和消费线程对缓冲区的互斥访问
# define N 100 //缓冲区大小
int count = 0; //缓冲区记录数
/* 生产者线程*/
void procedure(void){
int item; //缓冲区中数据项
while(true){
item = produce_item(); //产生下一个数据项
if(count == N){
sleep();
}
pthread_mutex_lock(&mutex);
insert_item(item); //新数据项放入缓冲区中
count = count + 1;
pthread_mutex_unlock(&mutex);
if(count == 1){ //如果插入前为空,唤醒消费者
wakeup(consumer);
}
}
}
/*消费者线程*/
void consumer(void){
int item;
while(true){
if(count == 0){
sleep();
}
pthread_mutex_lock(&mutex);
item = remove_item();
count = count -1;
pthread_mutex_unlock(&mutex);
if(count == N-1){ //缓冲区有空,唤醒生产者
wakeup(proceducer);
}
}
}
上面代码存在一个问题:消费者解锁到休眠这段代码有可能被打断
条件变量功能:把释放互斥锁到休眠当作是一个原子操作,不容打断
定义条件变量:
include <pthread.h>
pthread_cond_t cond;
条件变量初始化:
(静态)pthread_cond_t cond = PTHREAD_COND_INITIALIER;
(动态)int pthread_cond_init(pthread_cond_t *cond,pthread_condattr_t *cond_attr);
条件变量等待:
int pthread_cond_wait(pthread_cond_t *restrict cond,pthread_mutex_t *restrict mutex);
int pthread_cond_timedwait(pthread_cond_t *restrict cond,pthread_mutex_t *restrict mutex,const struct timespec *restrict abstime);
(等于或超过一个时间返回ETIME)
timespec定义:
typedef struct timespec{
time_t tv_sec; //s
long tv_nsex; //ns
}timespec_t;
唤醒等待一个条件变量:
int pthread_cond_signal(pthread_cond_t *cond);
唤醒所有等待该条件变量:
int pthread_cond_broadcast(pthread_cond_t *cond);
条件变量销毁:
int pthread_cond_destory(pthread_cond_t *cond);
2. 1~20除3测试
#include <stdio.h>
#include <unistd.h>
#include <pthread.h>
#include <sys/time.h>
#include <string.h>
#include <cstdlib>
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
void *thread1(void *);
void *thread2(void *);
int i = 1;
int main(){
pthread_t t_a;
pthread_t t_b;
pthread_create(&t_a,NULL,thread2,(void *)NULL);
pthread_create(&t_b,NULL,thread1,(void *)NULL);
pthread_join(t_b,NULL);
pthread_mutex_destroy(&mutex);
pthread_cond_destroy(&cond);
exit(0);
}
void *thread1(void *arg){
for(i=1;i<=20;++i){
pthread_mutex_lock(&mutex);
if(i % 3 == 0)
pthread_cond_signal(&cond);
else
printf("thread1:%d\n",i);
pthread_mutex_unlock(&mutex);
sleep(1);
}
}
void *thread2(void *arg){
while(i<20){
pthread_mutex_lock(&mutex);
if(i % 3 != 0)
pthread_cond_wait(&cond, &mutex); //会释放互斥锁,所以前面加锁,等到条件变量信号后,会对互斥锁加锁
printf("----------thread2:%d\n",i);
pthread_mutex_unlock(&mutex);
sleep(1);
i++;
}
}