自学内容网 自学内容网

【Linux】线程同步(第十七篇)

目录

线程同步:

1.概述

2.线程同步的方式

1.互斥锁:

2.读写锁:

3.进程互斥锁:

4.自旋锁:

5.信号量:

6.条件变量COND:


线程同步:

1.概述

多线程模型中,即当有一个线程在对内存进行操作时,其他线程都不可以对这个内存地址进行操作,直到该线程完成操作, 其他线程才能对该内存地址进行操作,而其他线程又处于等待状态。

需要安装:

sudo apt-get install manpages-posix
sudo apt-get install manpages-posix-dev

在man里面可以查询到线程同步的相关函数

2.线程同步的方式

1.互斥锁:

可以使用互斥锁对共享资源进行保护

老版本的互斥锁,占用线程解除占用,内核唤醒全部等待线程,多个线程竞争,获取锁资源(惊群效应)

内核会给等待队列中某个线程发送唤醒标记,内定下一个资源使用的线程 占用线程解除占用后,内核会确认队列,确认队列后发送唤醒标记(就近原则) 注意:某个线程持续占用,重复申请,那么大概率该线程一直占用锁资源

互斥锁涉及的函数

pthread_mutex_t lock //互斥锁类型,
pthread_mutex_init(pthread_mutex_t * lock,pthread_mutexattr_t * mattr); //互斥锁初始化
pthread_mutex_destroy(pthread_mutex_t * lock); //互斥锁销毁
pthread_mutex_lock(pthread_mutex_t * lock);//互斥锁加锁
pthread_mutex_unlock(pthread_mutex_t * lock);//互斥锁解锁

只在全局资源访问时加锁

示例:

两个线程同时执行100次 ++,执行结果不一定是200。因为会有线程并发的问题。

#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <sys/wait.h>
#include <unistd.h>
int ncount = 0;       
pthread_mutex_t mlock;
void* thread_joba(void *lpvoid)
{
     int i =0;
     while(i++ <5000)
     {
          usleep(100);
          pthread_mutex_lock(&mlock);
          printf("thread_joba is %x,++ncount = %d\n",(unsigned  int)pthread_self(),ncount++);
          pthread_mutex_unlock(&mlock);
     }
}
​
void* thread_jobb(void* lpvoid)
{
    
     int i =0;
     while(i++ <5000)
     {
          pthread_mutex_lock(&mlock);
          printf("thread_jobb is %x,++ncount = %d\n",(unsigned  int)pthread_self(),ncount++);
          pthread_mutex_unlock(&mlock);
          usleep(100);
     }
}
​
int main()
{
    pthread_t tid1,tid2;
    pthread_mutex_init(&mlock,NULL);
    pthread_create(&tid1,NULL,thread_joba,NULL);
    pthread_create(&tid2,NULL,thread_jobb,NULL);
    
    pthread_join(tid1,NULL);
    pthread_join(tid2,NULL);
    pthread_mutex_destroy(&mlock);
    return 0;
}

2.读写锁:

可以提高资源访问率,读写锁中写锁为(独占锁)只允许一个线程获取写锁,对全局资源进行写操作,读锁(共享锁)若干个线程可以获取读锁,对全局资源进行读访问 读写锁使用时,读写互斥(进行读操作时,不允许进行写操作,反之亦然)

读写锁涉及到的函数

pthread_rwlock_t lock //读写锁类型
pthread_rwlock_init(pthread_rwlock_t *lock,NULL) //初始化读写锁
pthread_rwlock_destroy(pthread_rwlock_t *lock) //销毁读写锁
pthread_rwlock_wrlock(pthread_rwlock_t *lock) //申请用写锁(独占锁)
pthread_rwlock_rdlock(pthread_rwlock_t *lock) //申请用读锁(共享锁)
pthread_rwlock_unlock(pthread_rwlock_t* lock) //解锁(独占或共享)

示例: 创建8个线程,共享使用一把读写锁,三个写线程使用写锁操作共享资源,五个读线程读访问共享资源

#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <sys/wait.h>
#include <unistd.h>
int ncount = 0;         
pthread_rwlock_t rwlock;
void* thread_joba(void *lpvoid)
{
     int i =0;
     while(i++ <5000)
     {
          usleep(100);
          pthread_rwlock_rdlock(&rwlock);
          printf("thread_joba is %x,read ncount = %d\n",(unsigned  int)pthread_self(),ncount);
          pthread_rwlock_unlock(&rwlock);
     }
}
​
void* thread_jobb(void* lpvoid)
{
    
     int i =0;
     while(i++ <5000)
     {
          pthread_rwlock_wrlock(&rwlock);
          printf("thread_jobb is %x, write ncount = %d\n",(unsigned  int)pthread_self(),ncount++);
          pthread_rwlock_unlock(&rwlock);
          usleep(100);
     }
}
int main()
{
    int i =0;
    pthread_t tid1,tid2;
    pthread_rwlock_init(&rwlock,NULL);
    for(i;i<3;i++)
    {
    pthread_create(&tid1,NULL,thread_joba,NULL);}
    for(i=3;i<8;i++)
    {
    pthread_create(&tid2,NULL,thread_jobb,NULL);}
    
    pthread_join(tid1,NULL);
    pthread_join(tid2,NULL);
    pthread_rwlock_destroy(&rwlock);
    return 0;
}

使用场合:log日志系统

快速定位错误异常,有人要写日志, 有人要读日志(排查异常)

3.进程互斥锁:

互斥锁默认情况下使用为线程互斥锁,可以通过设置互斥锁属性,将lock变为进程互斥锁,适用于多进程保护全局资源

pthread_mutexattr_t attr; //互斥锁属性类型
pthread_mutexattr_init(pthread_mutexattr_t *attr); //初始化互斥锁属性。默认时线程互斥锁
pthread_mutexattr_destroy(pthread_mutexattr_t* attr); //销毁释放互斥锁属性
//通过API将attr中的线程互斥锁属性改为进程互斥锁属性
pthread_mutexattr_setpshared(pthread_mutexattr_t* attr,int* pshared);//修改互斥锁属性中的互斥锁类型
pthread_mutexattr_getpshared(pthread mutexattr_t* attr,int* pshared);//获取互斥锁属性中的锁类型
​
pshared 锁类型:
  PTHREAD_PROCESS_SHARED //进程互斥锁
  PTHREAD_PROCESS_PRIVATE //线程互斥锁
​
pthread_mutex_init(&lock,&attr); //互斥锁属性准备完成后,在互斥锁初始化时使用

示例: 父子进程利用进程锁,保护全局资源(实现资源访问互斥)

#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <unistd.h>
#include <sys/mman.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
​
#define onepage 4096
​
typedef struct myshared
{
    pthread_mutex_t lock;
    int counter;
}myshared;
int main()
{   
     pid_t pid;
    //创建一个用于共享内存映射得文件
    int fd = open("mmapfile",O_RDWR|O_CREAT,0664);
    if(fd == -1)
     {
        printf("open failed \n");
        exit(0);
     }
    //将文件设置为myshared大小
     ftruncate(fd,sizeof(myshared));
     myshared *pshared  = (myshared *)mmap(NULL,sizeof(myshared),PROT_READ|PROT_WRITE,MAP_SHARED,fd,0);
    //设置互斥锁属性,变为进程互斥锁
    pthread_mutexattr_t lockattr;
    pthread_mutexattr_init(&lockattr);
    pthread_mutexattr_setpshared(&lockattr,PTHREAD_PROCESS_SHARED);
     
    pthread_mutex_init(&(pshared->lock),&lockattr);
    pid = fork();
    int i =0;
    if(pid >0)
    {
           
      for(i=0;i<50;i++)
      {
           pthread_mutex_lock(&pshared->lock);
           printf("parent process counter= %d\n",++pshared->counter);
           pthread_mutex_unlock(&pshared->lock);
      }
    
    }else if(pid ==0)
    {
         for(i =0;i<50;i++)
         {      
           pthread_mutex_lock(&pshared->lock);
           printf("child  process counter= %d\n",++pshared->counter);
           pthread_mutex_unlock(&pshared->lock);
         }
    }
    else
    {
        printf("fork failed\n");
        exit(0);
    }
    wait(NULL);
    pthread_mutex_destroy(&pshared->lock);
    pthread_mutexattr_destroy(&lockattr);
    munmap(pshared,sizeof(myshared));
    return 0;
}
​

4.自旋锁:

积极锁(不会阻塞),当某线程占用锁资源时,其他线程会不停自旋尝试获取锁资源,直到占用线程释放为止 优势:自旋锁可以提高资源的利用率,相比互斥锁可以更快的获取资源(与互斥锁不同没有一系列挂起唤醒操作,减少开销) 弊端:使用自旋锁的线程会长时间处于R状态,进线程开销较大

DEFINE_SPINLOCK(spinlock_t lock) 定义并初始化化一个自旋锁

int spin_lock_init(spinlock_t *lock) 初始化自旋锁

void spin_lock(spinlock_t *lock) 获取指定的自旋锁 即加锁

void spin_unlock(spinlock_t *lock) 释放自旋锁 或 解锁

int spin_trylock(spinlock_t *lock) 尝试获取指定的自旋锁,如果没有获取到就返回 0

int spin_is_locked(spinlock_t *lock) 检查指定的自旋锁是否被获取,如果没有被获取就返回非 0,否则返回 0

void spin_lock_irq(spinlock_t *lock) 禁止本地中断,并获取自旋锁

void spin_unlock_irq(spinlock_t *lock) 激活本地中断,并释放自旋锁

void spin_lock_irqsave(spinlock_t *lock,unsigned long flags) 保存中断状态,禁止本地中断,并获取自旋锁

void spin_unlock_irqrestore(spinlock_t*lock, unsigned long flags)

将中断状态恢复到以前的状态,并且激活本地中断,释放自旋锁 自旋锁简单使用 1 DEFINE_SPINLOCK(lock) 定义并初始化一个自旋锁

/* 线程 A */
 void functionA (){
     unsigned long flags;  /* 中断状态 */
     spin_lock_irqsave(&lock, flags) /* 获取锁 */
     /* 临界区 */
     spin_unlock_irqrestore(&lock, flags) /* 释放锁 */
 }
​
 /* 中断服务函数 */
 void irq() {
     spin_lock(&lock)  /* 获取锁 */
     /* 临界区 */
     spin_unlock(&lock) /* 释放锁 */
 }

5.信号量:

6.条件变量COND:

常见的一些线程并发模型中,多线程无关联,各自执行完成任务,条件变量可以通过指定条件,逻辑让多线程有一个交集场景,让开发者可以通过条件判断,控制工作线程

条件变量可以控制线程的挂起或唤醒(开发者利用条件制定规则,线程严格按照条件规则或执行任务,或等待)

pthread_cond_t  cd//条件变量类型
​
pthread_cond_init(pthread_cond_t *cd,NULL);//初始化条件变量
pthread_cond_destroy(pthread_cond_t *cd ) ; 
pthread_cond_wait(pthread_cond_t* cd, pthread_mutex_t *lock); //通过此函数,可以将线程挂起在特定的条件变量上 
pthread_cond_signal(pthread_cond_t *cd); //唤醒一个挂起在指定条件变量中的线程
​
pthread_cond_broadcast(pthread_cond_t* cd) //唤醒所有挂起在指定条件变量中的线程

示例:两个线程,对全局资源counter进行++操作,,但是区分工作,分奇数偶数

#include  <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <pthread.h>
#include <sys/types.h>
#include <sys/stat.h>
​
pthread_mutex_t mlock;
pthread_cond_t  ca;
pthread_cond_t  cb;
int ncounter = 0;
void* thread_job1(void* lpvoid)
{
    int i= 0;
    for(i;i <100;i++)
    {
       pthread_mutex_lock(&mlock);
       if(ncounter %2 ==0)
         {
              pthread_cond_wait(&ca,&mlock);
         }
         printf("thread job1 %x ncounter = %d\n",(unsigned int)pthread_self(),++ncounter);
        pthread_cond_signal(&cb);
       pthread_mutex_unlock(&mlock);
    }
}
​
void* thread_job2(void* lpvoid)
{
   
    int i= 0;
    for(i;i <100;i++)
    {
       pthread_mutex_lock(&mlock);
       if(ncounter %2 !=0)
         {
              pthread_cond_wait(&cb,&mlock);
         }
         printf("thread job2 %x ncounter = %d\n",(unsigned int)pthread_self(),++ncounter);
        pthread_cond_signal(&ca);
       pthread_mutex_unlock(&mlock);
    }
}
int main()
{ 
   
​
   pthread_t tid1,tid2;
   pthread_mutex_init(&mlock,NULL);
   pthread_cond_init(&ca,NULL);
   pthread_cond_init(&cb,NULL);
   pthread_create(&tid1,0,thread_job1,0);
   pthread_create(&tid2,0,thread_job2,0);
   
   pthread_join(tid1,NULL);
   pthread_join(tid2,NULL);
   pthread_mutex_destroy(&mlock);
   pthread_cond_destroy(&ca);
   pthread_cond_destroy(&cb);
   return 0;
}

pthread_cond_wait函数,第一次调用(挂起调用),挂起当前线程并解锁互斥锁 第二次调用(唤醒调用),上锁互斥锁


原文地址:https://blog.csdn.net/abclui/article/details/142383576

免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!