锁的注意事项
1、尽量保证锁的粒度,越小越好。(访问共享数据前,加锁,访问结束后立即解锁)
2、互斥锁,本质是结构体,但是可以看成整数,初值为1。(pthread_mutex_init调用成功)
3、加锁: --操作,阻塞线程
4、解锁:++操作,唤醒阻塞在锁上的进程
try锁:尝试加锁 , 成功-- , 失败:返回错误号(EBUSY),不阻塞。
读写锁(三句话)
读写锁只有一把,但是具备两种状态。
“写模式加锁”:解锁前,所有对该锁加锁的线程都会被阻塞。
“读模式加锁”:其他线程用读模式加锁会成功,写模式加锁则阻塞。
“读模式加锁”:既有试图写模式加锁的线程也有读模式加锁的线程,那么读写锁会阻塞随后的读模式锁请求,优先满足写模式锁,读写锁并行阻塞,写锁优先级高。
写独占、读共享。
相较于互斥量而言,当读线程多的时候,提高访问效率。锁一般都定义成全局变量。
主要应用函数pthread_rwlock_xxx
pthread_rwlock_t rwlock; 创建读写锁pthread_rwlock_init
pthread_rwlock_destroy
pthread_rwlock_rdlock
pthread_rwlock_wrlock
pthread_rwlock_tryrdlock
pthread_rwlock_trywrlock
pthread_rwlock_unlock成功返回0,失败返回错误号
3个线程不定时写同一全局资源 , 5个线程不定时读同一全局资源.
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
#include<pthread.h>pthread_rwlock_t rwlock;
int counter = 20;void* th_write(void* arg){int t;int ret;int i = (int)arg;while(1){ret = pthread_rwlock_wrlock(&rwlock);if(ret != 0){fprintf(stderr , "pthread_rwlock_wrlock error with :%s\n" , strerror(ret));exit(1);}t = counter;usleep(1000);printf("-----write %d: %lu: counter=%d , counter++ = %d\n" , i , pthread_self() , t , ++counter);ret = pthread_rwlock_unlock(&rwlock);if(ret != 0){fprintf(stderr , "pthread_rwlock_unlock error with :%s\n" , strerror(ret));exit(1);}usleep(10000);}return NULL;
}void* th_read(void* arg){int i = (int)arg;int ret;while(1){ret = pthread_rwlock_rdlock(&rwlock);if(ret != 0){fprintf(stderr , "pthread_rwlock_rdlock error with :%s\n" , strerror(ret));exit(1);}printf("-------read %d: %lu: counter = %d\n", i , pthread_self() , counter);ret = pthread_rwlock_unlock(&rwlock);if(ret != 0){fprintf(stderr , "pthread_rwlock_unlock error with :%s\n" , strerror(ret));exit(1);}usleep(2000);}return NULL;
}int main(int argc , char *argv[])
{pthread_t tid[8];int ret ;int i ;ret = pthread_rwlock_init(&rwlock , NULL);if(ret != 0){fprintf(stderr , "pthread_rwlock_init error with :%s\n" , strerror(ret));exit(1);}for(i =0 ; i < 3 ; i++ ){ret = pthread_create(&tid[i], NULL , th_write ,(void*)i);if(ret != 0){fprintf(stderr , "pthread_create error with :%s\n" , strerror(ret));exit(1);}}for(i = 0 ; i < 5 ; i++){ret = pthread_create(&tid[i+3] , NULL , th_read , (void*)i);if(ret != 0){fprintf(stderr , "pthread_create error with :%s\n" , strerror(ret));exit(1);}}for(i = 0 ; i < 8 ; i++){pthread_join(&tid[i] , NULL);if(ret != 0){fprintf(stderr , "pthread_join error with :%s\n" , strerror(ret));exit(1);}}pthread_rwlock_destroy(&rwlock);return 0 ;
}
死锁
使用锁不恰当导致的现象。条件
1、线程试图对同一个互斥量加锁两次
2、线程1拥有A锁 , 请求获得B锁 , 线程2拥有B锁,请求获得A锁。
条件变量
本身不是锁,但是通常要结合锁来使用。(等待某一个变量满足)。
pthread_cond_t cond; 创建读写锁pthread_cond_init
pthread_cond_destroy
pthread_cond_wait
pthread_cond_timewait
pthread_cond_signal
pthread_cond_broadcast成功返回0,失败返回错误号
pthread_cond_wait函数
阻塞等待一个条件变量
int pthread_cond_wait(&cond , pthread_mutex_t *restrict murex);
函数作用:
1、阻塞等待条件变量cond满足。
2、释放已掌握的互斥锁(解锁),相当于pthread_mutex_unlock(&mutex); 1,2两步为一个原子操作。
3、当被唤醒,pthread_cond_wait函数返回时,解除阻塞并重新申请获取互斥锁Pthread_mutex_lock(&mutex).
生产者、消费者模型
pthread_cond_signal函数
唤醒阻塞在条件变量上的一个线程
pthread_cond_broadcast函数
唤醒阻塞在条件变量上的多个线程
要求借助条件变量完成生产者消费者模型
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
#include<pthread.h>struct msg{int num;struct msg* next;
};pthread_mutex_t mutex;
pthread_cond_t has_data = PTHREAD_COND_INITIALIZER;struct msg * head;void err_thread(int ret , char*str)
{if(ret != 0){fprintf(stderr , "%s:%s\n" ,str , strerror(ret));pthread_exit(NULL);}
}void* produser(void* arg)
{int ret ;while(1){struct msg *mp;mp = malloc(sizeof(struct msg));mp->num = rand() % 1000 + 1;pthread_mutex_lock(&mutex);if(ret != 0)err_thread(ret , "pthread_mutex_lock error");head = mp;printf("------prod:%d , pid:%lu\n" , mp->num , pthread_self());pthread_mutex_unlock(&mutex);if(ret != 0)err_thread(ret , "pthread_mutex_unlock error");ret = pthread_cond_signal(&has_data);if(ret != 0)err_thread(ret , "pthread_mutex_lock error");sleep(rand() % 3);}return NULL;
}void* consumer(void* arg)
{int ret;while(1){struct msg *mp;mp = head;ret = pthread_mutex_lock(&mutex);if(ret != 0)err_thread(ret , "pthread_mutex_lock error");while(head == NULL){ret = pthread_cond_wait(&has_data , &mutex); // pthread_cond_wait函数返回时,重新加锁mutex。if(ret != 0 )err_thread(ret , "pthread_con_wait error");}mp = head;head = mp->next;ret = pthread_mutex_unlock(&mutex);if(ret != 0)err_thread(ret , "pthread_mutex_lock error");printf("-------cons:%d , cid:%lu\n" , mp->num , pthread_self());sleep(rand()%3);free(mp);}return NULL;
}int main(int argc , char *argv[])
{pthread_t pid , cid;srand(time(NULL));int ret;ret = pthread_mutex_init(&mutex , NULL);if(ret != 0)err_thread(ret , "pthread_create error");ret = pthread_create(&pid , NULL , produser , NULL);if(ret != 0)err_thread(ret , "pthread_create error");ret = pthread_create(&cid , NULL , consumer , NULL);if(ret != 0)err_thread(ret , "pthread_create error");pthread_join(pid , NULL);pthread_join(cid , NULL);return 0 ;
}
信号量
应用于线程、进程间同步。
相当于初始化值为 N 的互斥量。 N 为可同时访问的进程数。
sem_init()
sem_destroy()
sem_wait() //相当于加锁
sem_trywait()
sem_timewait()
sem_post() //相当于解锁返回值:成功返回0 , 失败返回errno
sem_wait 信号量>0,信号量-- ;信号量 = 0 ,线程阻塞。 对比pthread_mutex_lock()。
sem_post 信号量++, 唤醒阻塞在信号量上的线程
信号量的初值,决定了占用信号量的线程个数。
sem_init()函数
int sem_init(sem_t *sem , int pshared , unsigned int value);pshared: 0:用于线程间同步1:用于进程间同步value :N值 (指定同时访问的数目)
信号量的生产者消费者模型
主要是明白wait和post的关系,以及阻塞在哪的关系。
重点:
sem_wait 信号量>0,信号量-- ;信号量 = 0 ,线程阻塞。 对比pthread_mutex_lock()。
sem_post 信号量++, 唤醒阻塞在信号量上的线程
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
#include<pthread.h>
#include<semaphore.h>
#define NUM 5
int queue[NUM];
sem_t blank_number;
sem_t stat_number;void err_thread(int ret , char*str)
{if(ret != 0 ){fprintf(stderr , "%s:%s\n" , str , strerror(ret));pthread_exit(NULL);}
}void* produser(void* arg)
{int i = 0;while(1){sem_wait(&blank_number);//空格子数--queue[i] = rand()%1000 + 1;printf("---produce:%d\n" , queue[i]);sem_post(&stat_number); // 产品数++i = (i+1) % NUM; // 借助队列下标实现环形队列sleep(rand()%1);}return NULL;
}void* consumer(void* arg)
{int i = 0;while(1){sem_wait(&stat_number);printf("-----consume:%d\n" , queue[i]);queue[i] = 0;sem_post(&blank_number);i = (i + 1)% NUM;sleep(rand()%3);}return NULL;
}int main(int argc , char *argv[])
{pthread_t pid , cid;int ret ;srand(time(NULL));sem_init(&blank_number , 0 , NUM);sem_init(&stat_number , 0 , 0);ret = pthread_create(&pid , NULL , produser , NULL);if(ret != 0)err_thread(ret , "pthread_create error");ret = pthread_create(&cid , NULL , consumer , NULL);if(ret != 0)err_thread(ret , "pthread_create error");pthread_join(pid , NULL);pthread_join(cid , NULL);sem_destroy(&blank_number);sem_destroy(&stat_number);return 0 ;
}
系统编程完结撒花!开始网络编程咯~