linux 线程同步

线程同步:

    协同步调,对公共区域的数据排队访问。防止数据混乱。产生与时间有关的错误。


    一个线程发出某一功能调用时,在没有得到结果之前,该调用不返回。同时其他线程为保证数据一致性,不能调用该功能。
    
   列子:银行存款5000.柜台存折:取3000; 提款机,卡 :取 3000 。剩余:2000

锁的使用:        
    
    建议锁 ! 对公共数据进行保护。所有线程【应该】在访问公共数据前先拿锁再访问。但是,锁本身不具有强制性。

使用mutex(互斥量 互斥锁)一般步骤:

        

    1.pthread_mutex_t lock;  pthread_mutex_t 本质是一个结构体 为了方便想象成 int

    2.pthread_mutex_init; 初始化

    int pthread_mutex_init(pthread_mutex_t *restrict mutex,

              const pthread_mutexattr_t *restrict attr);


    3.pthread_mutex_lock; 加锁

    4.访问共享数据(stdout)

    5.pthread_mutex_unlock();解锁

    6.pthread_mutex_destroy;销毁锁

    都是成功返回0 失败返回 errno


    初始化互斥量:

        pthread_mutex_t mutex;
    
        1. pthread_mutex_init(pthread_mutex_t *restrict mutex,

              const pthread_mutexattr_t *restrict attr);


        2.pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER

//模拟多线程操作共享资源
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<string.h>
#include<time.h>
#include<pthread.h>
pthread_mutex_t mutex;//定义一把互斥锁可以想象为一个 int 数据
void* tfn(void* arg){
  srand(time(NULL));
  while(1){
  //  pthread_mutex_lock(&mutex);//加锁  可以想象成 锁 -- 1 --》 0
    printf("hello ");
    sleep(rand()%3);//模拟长时间操作共享资源,导致cpu易主,产生与时间有关的错误
    printf("world\n");
   // pthread_mutex_unlock(&mutex);//解锁  可以想象成 锁++ (0++ --》1);
    sleep(rand()%3);
  }
  return NULL;
}

int main(){
  pthread_t tid;
  srand(time(NULL));
  int ret = pthread_mutex_init(&mutex,NULL);//初始化互斥锁 可以认为 锁的值 为 1
  if(ret!=0){
    fprintf(stderr,"pthread_mutex_init error %s\n",strerror(ret));
    exit(1);
  }
  pthread_create(&tid,NULL,tfn,NULL);
  while(1){
    //pthread_mutex_lock(&mutex);
    printf("HELLO ");
    sleep(rand()%3);
    printf("WORLD\n");
    //pthread_mutex_unlock(&mutex);
    sleep(rand()%3);
  }
  pthread_join(tid,NULL);
 pthread_mutex_destroy(&mutex);//销毁互斥锁
  



  return 0;

}

未加锁输出:

HELLO hello WORLD
world
HELLO WORLD
HELLO hello WORLD
world
HELLO WORLD
hello world
HELLO hello WORLD
world
HELLO WORLD
hello world
hello HELLO WORLD
   

加锁后: 

HELLO WORLD
hello world
hello world
hello world
HELLO WORLD
hello world
 

注意事项: 

        尽量保证锁的粒度,越小越好。(访问共享数据前加锁。访问结束立即 解锁。)

        互斥锁,初值为  1. (pthread_mutex_init()函数调用成功)

        加锁: --操作

        解锁: ++操作

        try锁: 尝试加锁。 成功--。 失败 ,返回错误号(EBUSY)不阻塞
restrict关键字:    

    用来限定指针变量。被该关键字限定的指针变量所指向的内存操作,必须由本指针完成


死锁 :

    使用锁不恰当导致的现象

        1.对一个锁反复lock (解决:加完锁 立即解锁)

        2.两个线程 ,各自持有一把锁,请求另一把  (解决:trylock  当不能获取所有的锁时,放弃已经占有的锁)


读写锁:


    锁只有一把。以读方式给数据加锁--读锁  。以写方式给数据加锁 --写锁

    读共享 ,写独占

    写锁优先级高

    相较于互斥量而言,当读线程多时,提高访问效率


    pthread_rwlock_t rwlock;

    pthread_rwlock_init(&rwlock,NULL)

    

     pthread_rwlock_wrlock(&rwlock);

    pthread_rwlock_unlock(&rwlock);

     pthread_rwlock_rdlock(&rwlock);

     pthread_rwlock_unlock(&rwlock);


    pthread_rwlock_destroy(&rwlock);


    成功返回 0  失败 errno

读写锁特性:


    写模式加锁时,解锁前,所有对该锁加锁的线程都会被阻塞

    读模式加锁时,如果线程以读模式对其加锁会成功;如果线程以写模式加锁会阻塞

    读模式加锁时,既有试图以写模式加锁的线程,也有试图以读模式加锁的线程。读写锁会阻塞随后的读模式锁请求。优先满足写模式。

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
#include<pthread.h>


int counter;

pthread_rwlock_t rwlock;
//3个线程不定时写同一全局资源 5个线程不定时读同一全局资源

void* th_write(void* arg){
  int t;
  int i= (int)arg;
  while(1){
    pthread_rwlock_wrlock(&rwlock);//写模式加锁 写独占
    t=counter;
    usleep(1000);
    printf("=========write %d: %lu : ++ counter =%d\n",i,pthread_self(),++counter);
    pthread_rwlock_unlock(&rwlock);
    usleep(10000);
  }
  return NULL;

}
void* th_read(void* arg){
  int i= (int)arg;
  while(1){
    pthread_rwlock_rdlock(&rwlock);//读线程间 ,读锁共享
    printf("----------------read %d: %lu: %d\n",i,pthread_self(),counter);
    pthread_rwlock_unlock(&rwlock);
    usleep(2000);
  }
  return NULL;
}

int main(){
  int i;

  pthread_t tid[8];

  pthread_rwlock_init(&rwlock,NULL);
  for(i=0;i<3;++i)
    pthread_create(&tid[i],NULL,th_write,(void*)i);
  for(i=0;i<5;++i)
    pthread_create(&tid[i+3],NULL,th_read,(void*)i);
  for(i=0;i<8;++i)
    pthread_join(tid[i],NULL);


  pthread_rwlock_destroy(&rwlock);

  return 0;
}

输出:

--------------read 3: 140386488055552: 267
----------------read 0: 140386513233664: 267
----------------read 1: 140386504840960: 267
----------------read 4: 140386479662848: 267
=========write 0: 140386538411776 : ++ counter =268
=========write 1: 140386530019072 : ++ counter =269
----------------read 4: 140386479662848: 269
----------------read 0: 140386513233664: 269
----------------read 1: 140386504840960: 269
----------------read 3: 140386488055552: 269
----------------read 2: 140386496448256: 269
----------------read 4: 140386479662848: 269
----------------read 0: 140386513233664: 269
----------------read 1: 140386504840960: 269
----------------read 3: 140386488055552: 269
----------------read 2: 140386496448256: 269
 

条件变量:

    本身不是锁!但是通常结合锁来使用 。 mutex

    pthread_cond_t cond;

    初始化条件变量:

        1.pthread_cond_init(&cond,NULL); 动态初始化

        2.pthread_cond_t cond = PTHREAD_COND_INITIALIZER  ; 静态初始化

    pthread_cond_signal(); 唤醒阻塞在条件变量上的(至少)一个线程

    pthread_cond_broadcast();唤醒阻塞在条件变量上的所有线程

 

//条件变量 互斥量 实现生产者消费者模型

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
#include<pthread.h>
#include<time.h>

void err_thread(int ret,const char* str){
  if(ret!=0){
    fprintf(stderr,"%s :%s\n",str,strerror(ret));
    pthread_exit(NULL);
  }
}
struct msg{
  int num;
  struct msg* next;
};
struct msg* head;
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;//定义初始化一个互斥量
pthread_cond_t has_data = PTHREAD_COND_INITIALIZER;//定义初始化一个条件变量

void* producer(void* arg){
  (void)arg;
  while(1){

  
  struct msg* mp =(struct msg*)malloc(sizeof(struct msg));
  mp->num=rand()%1000+1;      //模拟生产一个数据
  printf("--produce %d\n",mp->num);

  pthread_mutex_lock(&mutex); //加锁
  mp->next=head;
  head=mp;
  pthread_mutex_unlock(&mutex);//解锁

  pthread_cond_signal(&has_data);  //唤醒阻塞在条件变量 has_data 上的线程

  
  sleep(rand()%3);
  }
  return NULL;
}

void* consumer(void* arg){
  (void)arg;
  while(1){
  struct msg* mp;
  pthread_mutex_lock(&mutex);//加锁
  while(head ==NULL){
    pthread_cond_wait(&has_data,&mutex);
  }
  mp=head;
  head=mp->next;
  pthread_mutex_unlock(&mutex);  //解锁互斥量
  printf("-----consumer id:%lu:%d\n",pthread_self(),mp->num);
  free(mp);
  sleep(rand()%3);
  }
  return NULL;
}
int main(){
  pthread_t pid,cid;
  int ret;
  srand(time(NULL));
  ret = pthread_create(&pid,NULL,producer,NULL);//生产者
  if(ret!=0)
    err_thread(ret,"pthread_create producer error");
  ret = pthread_create(&cid,NULL,consumer,NULL);// 消费者
  if(ret!=0)
    err_thread(ret,"pthread_create consumer error");
  ret = pthread_create(&cid,NULL,consumer,NULL);// 消费者
  if(ret!=0)
    err_thread(ret,"pthread_create consumer error");
  ret = pthread_create(&cid,NULL,consumer,NULL);// 消费者
  if(ret!=0)
    err_thread(ret,"pthread_create consumer error");
  


  pthread_join(pid,NULL);
  pthread_join(cid,NULL);

  return 0;

}

输出:

--produce 837
-----consumer id:140241653298944:837
--produce 76
-----consumer id:140241644906240:76
--produce 358
-----consumer id:140241644906240:358
--produce 764
--produce 538
-----consumer id:140241644906240:538
-----consumer id:140241653298944:764
 

注意:当有多个消费者时 条件变量判断 用 while 循环

信号量:

    相当于 初始值为 N 的 互斥量。N值表示可以同时访问共享数据区的线程数。


    函数:

        sem_init();

        int sem_init(sem_t *sem, int pshared, unsigned int value);

        参数:

            sem:信号量

            pshared:     0:用于线程间同步

                    1:用于进程间同步

            value: N值 。(指定同时访问你的线程数)                        


        sem_destroy(sem_t *sem);

        sem_wait(sem_t *sem);  一次调用 ,做一次 -- 操作,当信号量的值为 0 时, 再次-- 就会阻塞。(对比 phread_mutex_lock)

        sem_post(sem_t *sem); 一次调用 ,做一次 ++操作,当信号量的值为 N 时, 再次++ 就会阻塞。(对比 phread_mutex_unlock)

//信号量实现生产者消费者模型
#include <semaphore.h>
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
#include<pthread.h>


#define NUM 5
int queue[NUM];                    //全局数据实现环形队列
sem_t blank_number,product_number; //空格子信号量 ,产品信号量

void err_thread(int ret,const char* str){
  if(ret!=0){
    fprintf(stderr,"%s :%s\n",str,strerror(ret));
    pthread_exit(NULL);
  }
}
void* producer(void* arg){
  int i= 0;

  while(1){
    sem_wait(&blank_number);  //生产者将空格子数--,为0则阻塞等待
    queue[i] = rand()%1000+1;
    printf("-----Produce ---%d\n",queue[i]);
    sem_post(&product_number);//将产品数++
    
    i = (i+1)%NUM;  //借助下标实现环形
    sleep(rand()%1);

  }


}
void* consumer(void* arg){
  int i =0;
   
  while(1){
    sem_wait(&product_number);  //消费者将产品数-- ,为0 则阻塞等待
    printf("--Consume ---%d\n",queue[i]);
    queue[i]= 0;   //消费一个产品
    sem_post(&blank_number);  //消费掉以后,将空格子数++

    i = (i+1)% NUM;
    sleep(rand()%3);
  }
}
int main(){
  pthread_t pid,cid;
  int ret;
  sem_init(&blank_number,0,NUM); //初始化空格子信号量为5 ,线程将共享 ---- 0
  sem_init(&product_number,0,0); //产品数为0
  srand(time(NULL));
  ret = pthread_create(&pid,NULL,producer,NULL);//生产者
  if(ret!=0)
    err_thread(ret,"pthread_create producer error");
  ret = pthread_create(&cid,NULL,consumer,NULL);// 消费者
  if(ret!=0)
    err_thread(ret,"pthread_create producer error");
  pthread_join(pid,NULL);
  pthread_join(cid,NULL);

  sem_destroy(&blank_number);
  sem_destroy(&product_number);

  return 0;
}

    

     输出:

-----Produce ---152
-----Produce ---373
-----Produce ---22
-----Produce ---681
-----Produce ---506
--Consume ---152
-----Produce ---779
--Consume ---373
--Consume ---22
--Consume ---681


 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值