1.互斥锁
互斥锁的使用:
定义锁:pthread_mutex_t mutex
初始化锁:int pthread_mutex_init(&mutex,NULL)
加锁:int pthread_mutex_lock(阻塞加锁)
int pthread_mutex_trylock(在锁被占用时,返回EBUSY,而不是挂起等待)
解锁:int pthread_mutex_unlock(&mutex)
销毁:int pthread_mutex_destroy(&mutex)
**注意:多线程的互斥因为没有使用互斥锁,会导致在一个线程写完11111后跑到另一个线程写33333。使用互斥锁,可以避免这种情况发生。
1)采用全局变量
#include <stdio.h>
#include <pthread.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdlib.h>
#include <string.h>
#define N 10000
pthread_mutex_t mutex;
void* task1(void * arg)
{
int i = 0;
int fd = *((int*)arg);
while(i++ < N){
pthread_mutex_lock(&mutex);
write(fd,"11111",5);
write(fd,"22222\n",6);
pthread_mutex_unlock(&mutex);
}
return NULL;
}
void* task2(void * arg)
{
int i = 0;
int fd = *((int*)arg);
while(i++ < N){
pthread_mutex_lock(&mutex);
write(fd,"33333",5);
write(fd,"44444\n",6);
pthread_mutex_unlock(&mutex);
}
return NULL;
}
int main(){
pthread_t tid1,tid2;
int fd;
fd = open("/home/ben/c.d/t1.txt",O_RDWR|O_CREAT|O_APPEND,0755);
if(fd == -1){
printf("open the file fail");
exit(1);
}else{
printf("open successful!\n");
}
pthread_mutex_init(&mutex,NULL);
if(pthread_create(&tid1,NULL,task1,&fd)){
perror("pthread_create error!");
exit(1);
}
printf("create thread successful! tid1=%ld\n",tid1);
if(pthread_create(&tid2,NULL,task2,&fd) != 0){
perror("pthread_create error!");
exit(1);
}
printf("create thread successful!tid2=%ld\n",tid2);
pthread_join(tid1,NULL);
pthread_join(tid2,NULL);
printf("main join successful!\n");
pthread_mutex_destroy(&mutex);
printf("end...");
close(fd);
return 0;
}
2)采用参数传递
用结构体定义的变量叫结构体变量,如:
struct student stu; //定义一个结构体变量stu
这种变量在引用结构体成员时,使用点(.)来操作。
结构体类型也可以定义指针变量,如:
struct student *pstu; //定义一个结构体指针变量pstu
pstu=&stu ; //pstu指针指向stu结构体变量
结构体指针变量在引用成员变量时,使用箭头(->)来操作。
#include <stdio.h>
#include <pthread.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdlib.h>
#include <string.h>
#define N 10000
typedef struct{
pthread_mutex_t mutex;
int fd;
}Msg;
void* task1(void * arg)
{
Msg * msgp = (Msg*)arg; //这里的参数是一个指针变量,所有我们定义的结构体变量是指针型的
int fd = msgp->fd;
int i = 0;
while(i++ < N){
pthread_mutex_lock(&msgp->mutex);
write(fd,"11111",5);
write(fd,"22222\n",6);
pthread_mutex_unlock(&msgp->mutex);
}
return NULL;
}
void* task2(void * arg)
{
Msg * msgp = (Msg*)arg;
int fd = msgp->fd;
int i = 0;
while(i++ < N){
pthread_mutex_lock(&msgp->mutex);
write(fd,"33333",5);
write(fd,"44444\n",6);
pthread_mutex_unlock(&msgp->mutex);
}
return NULL;
}
int main(){
int fd = open("/home/ben/c.d/t.txt",O_RDWR|O_CREAT|O_APPEND,0755);
if(fd == -1){
printf("open the file fail");
exit(1);
}else{
printf("open successful!\n");
}
Msg msg;
msg.fd = fd;
pthread_t tid1,tid2;
fd = open("/home/ben/c.d/t.txt",O_RDWR|O_CREAT|O_APPEND,0755);
if(fd == -1){
printf("open the file fail");
exit(1);
}else{
printf("open successful!\n");
}
pthread_mutex_init(&msg.mutex,NULL);
if(pthread_create(&tid1,NULL,task1,&msg)){
perror("pthread_create error!");
exit(1);
}
printf("create thread successful! tid1=%ld\n",tid1);
if(pthread_create(&tid2,NULL,task2,&msg) != 0){
perror("pthread_create error!");
exit(1);
}
printf("create thread successful!tid2=%ld\n",tid2);
pthread_join(tid1,NULL);
pthread_join(tid2,NULL);
printf("main join successful!\n");
pthread_mutex_destroy(&msg.mutex);
printf("end...");
close(fd);
return 0;
}
2.线程信号量
使用步骤:
1)声明信号量:可以一起声明多个:sem_t 或 sem[n]
2)初始化信号量:sem_init(&sem,0,1) //第二个参数必须为0,表示这里是线程的信号量,为1表示进程的信号量,但还未实现有bug。
3)P操作:sem_wait(&sem)
4)V操作:sem_post(&sem)
5)删除信号量:sem_destroy(&sem)
加信号量互斥—实现互斥取款
#include <stdio.h>
#include <pthread.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdlib.h>
#include <string.h>
#include <semaphore.h>
int balance = 10;
sem_t sem1;
void* task11(void * arg)
{
while(balance > 0){
sem_wait(&sem1);
if(balance > 0){
printf("task11:before draw money,balance is %d\n",balance);
balance--;
printf("task11:after draw money,balance is %d\n",balance);
}
sem_post(&sem1);
sleep(1);
}
return NULL;
}
void* task22(void * arg)
{
while(balance > 0){
sem_wait(&sem1);
if(balance > 0){
printf("task22:before draw money,balance is %d\n",balance);
balance--;
printf("task22:after draw money,balance is %d\n",balance);
}
sem_post(&sem1);
sleep(1);
}
return NULL;
}
int main(){
sem_init(&sem1,0,1);
pthread_t tid1,tid2;
int fd;
fd = open("/home/ben/c.d/t.txt",O_RDWR|O_CREAT|O_APPEND,0755);
if(fd == -1){
printf("open the file fail");
exit(1);
}else{
printf("open successful!\n");
}
if(pthread_create(&tid1,NULL,task11,&fd)){
perror("pthread_create error!");
exit(1);
}
printf("create thread successful! tid1=%ld\n",tid1);
if(pthread_create(&tid2,NULL,task22,&fd) != 0){
perror("pthread_create error!");
exit(1);
}
printf("create thread successful!tid2=%ld\n",tid2);
pthread_join(tid1,NULL);
pthread_join(tid2,NULL);
printf("main join successful!\n");
printf("end...");
close(fd);
return 0;
}
加信号量实现同步
#include <stdio.h>
#include <pthread.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdlib.h>
#include <string.h>
#include <semaphore.h>
sem_t sem[3];
void* task1(void * arg)
{
while(1){
sem_wait(&sem[0]);
printf("1\n");
sleep(1);
sem_post(&sem[1]);
}
return NULL;
}
void* task2(void * arg)
{
while(1){
sem_wait(&sem[1]);
printf("2\n");
sleep(1);
sem_post(&sem[2]);
}
return NULL;
}
void* task3(void * arg)
{
while(1){
sem_wait(&sem[2]);
printf("3\n");
sleep(1);
sem_post(&sem[0]);
}
return NULL;
}
int main(){
sem_init(&sem[0],0,1);
sem_init(&sem[1],0,0);
sem_init(&sem[2],0,0);
pthread_t tid1,tid2,tid3;
if(pthread_create(&tid1,NULL,task1,NULL)){
perror("pthread_create error!");
exit(1);
}
printf("create thread successful! tid1=%ld\n",tid1);
if(pthread_create(&tid2,NULL,task2,NULL) != 0){
perror("pthread_create error!");
exit(1);
}
printf("create thread successful! tid2=%ld\n",tid2);
if(pthread_create(&tid3,NULL,task3,NULL) != 0){
perror("pthread_create error!");
exit(1);
}
printf("create thread successful! tid3=%ld\n",tid3);
pthread_join(tid1,NULL);
pthread_join(tid2,NULL);
pthread_join(tid3,NULL);
printf("main join successful!\n");
return 0;
}
互斥锁与信号量的区别:
Mutex 是一把钥匙,一个人拿了就可以进入一个房间,出来的时候把钥匙交给队列的第一个。
Semaphore是一件可以容纳N人的房间,如果人不满就可以进去,如果人满了,就要等待有人出来。队友N=1的情况,称为binarary semaphore。
Binary semaphore与Mutex的差异:
1.mutex要由获得锁的线程来释放(谁获得谁释放)。而semaphore可以由其他线程释放。
2.初始状态可能不一样:mutex的初始值为1,semaphore可能是0(或者是1)。
互斥信号量+条件变量
例题:有一个盘子,其中最多可以放6个馒头。有若干个生产者不断生产馒头,并放在盘子中,但不能超过6个。如果已经满6个,试图生产的生产者将会等待,直到盘中的馒头少于6个后再生产。同时,有若干个消费者,不断来消费盘中的馒头,盘中馒头不能少于0个。如果已经为0个,试图消费的消费者将会等待,直到盘中的馒头大于0个时再消费。
注:(一个生产者生产时另外一个生产者就不能生产,其他消费者也不能消费,同理一个消费者消费时,另一个消费者不能消费,生产者不能生产)
pthread_cond_wait总和一个互斥锁结合使用。在调用pthread_cond_wait前要先获取锁。pthread_cond_wait函数执行时先自动释放指定的锁,然后等待条件变量的变化。在函数调用返回之前,自动将指定的互斥量重新锁住。
int pthread_cond_signal(pthread_cond_t * cond);
pthread_cond_signal通过条件变量cond发送消息,若多个消息在等待,它只唤醒一个。pthread_cond_broadcast可以唤醒所有。调用pthread_cond_signal后要立刻释放互斥锁,因为pthread_cond_wait的最后一步是要将指定的互斥量重新锁住,如果pthread_cond_signal之后没有释放互斥锁,pthread_cond_wait仍然要阻塞。
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <semaphore.h>
#include <string.h>
int n = 0;// 0 <= n <= 6
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t wait_produce = PTHREAD_COND_INITIALIZER;
pthread_cond_t wait_consume = PTHREAD_COND_INITIALIZER;
void *producer(void * arg){
while(1){
char *i = arg;
pthread_mutex_lock(&mutex); //生产者获得锁
if(n == 6){
pthread_cond_wait(&wait_produce,&mutex);//生产者等待生产,并释放锁,进入消费者消费模式
}
if(n < 6){
printf("%s before produce:n = %d\n",i,n);
n++;
printf("%s after produce:n = %d\n",i,n);
}
pthread_cond_signal(&wait_consume); //唤醒消费者
pthread_mutex_unlock(&mutex);
// usleep(1);
}
pthread_exit(NULL);
}
void *comsumer(void * arg){
while(1){
char *i = arg;
pthread_mutex_lock(&mutex); //消费者获得锁
if(n == 0){
pthread_cond_signal(&wait_produce); //唤醒生产者
pthread_cond_wait(&wait_consume,&mutex); //消费者等待消费,并释放锁
}
if(n > 0){
printf("%s before consumer:n = %d\n",i,n);
n--;
printf("%s after comsumer:n = %d\n",i,n);
}
pthread_mutex_unlock(&mutex); //解锁
// usleep(1);
}
pthread_exit(NULL);
}
int main(){
pthread_t cid1,cid2;
pthread_t pid1,pid2;
if(pthread_create(&cid1,NULL,comsumer,"c1") != 0){
perror("pthread create c1 error\n");
}
if(pthread_create(&cid2,NULL,comsumer,"c2") != 0){
perror("pthread create c2 error\n");
}
if(pthread_create(&pid1,NULL,producer,"p1") != 0){
perror("pthread create p1 error\n");
}
if(pthread_create(&pid2,NULL,producer,"p2") != 0){
perror("pthread create p2 error\n");
}
pthread_join(cid1,NULL);
pthread_join(cid2,NULL);
pthread_join(pid1,NULL);
pthread_join(pid2,NULL);
//destroy pthread_cond
pthread_cond_destroy(&wait_produce);
pthread_cond_destroy(&wait_consume);
pthread_mutex_destroy(&mutex);
pthread_exit(NULL);
printf("main is over!\n");
}