欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

4.7、线程间的同步互斥

程序员文章站 2023-12-25 23:56:09
...

线程操作优点:由于由全局变量(共用),可以在各个线程间访问相同空间,线程之间的通信比较方便

线程操作的缺点:线程间的同行需要采用同步互斥机制(线程操作随机执行,导致通信空间值的不确定性)

线程通信:采用同步互斥方式实现线程间的通信
一、同步:多个任务按照一定的先后顺序,协调工作,去执行一件事情(程序的相应代码)

用信号量(一个值)去实现同步;
由信号量去控制线程是阻塞还是继续执行

信号量:代表着一种资源数目,这个值最小为0
信号量:
1、申请资源//p操作(让线程往下执行)信号量-1
2、释放资源//v操作(通过信号量添加一个资源,提供线程往下执行的功能)信号量+1

p操作:
if(信号量-1){
	线程往下执行
}
else{
	等待申请资源
}
v操作:
释放一个资源:信号量+1
{
	唤醒一个等待资源的线程让它继续执行
}

2、互斥:某个线程(地方)执行时,其他任何地方都不能执行,当前只有一个地方能够执行

通常情况用在资源竞争中,也是特殊的信号量
互斥锁的方式去实现

线程同步

  • 信号量的初始化
int sem_init(sem_t *sem, int pshared, unsigned int value);
/****************************************************************
参数:1、指定设置的信号量是哪个(传入信号量的地址)
     2、设置信号量在进程间操作还是线程间操作:设置为0表示线程间操作,非0表示进程间操作
     3、设置信号量的初始值
返回值:成功返回0,失败返回-1
****************************************************************/
  • 申请资源//p操作
int sem_wait(sem_t *sem);
/****************************************************************
参数:指定要操作哪个信号量执行申请资源(-1)
返回值:成功返回0,失败返回-1
****************************************************************/
  • 释放资源//v操作
int sem_post(sem_t *sem);
/****************************************************************
参数:指定要操作哪个信号量执行释放资源(+1)
返回值:成功返回0,失败返回-1
****************************************************************/

线程互斥

  • 初始化互斥锁
int pthread_mutex_init(pthread_mutex_t *mutex, const pthread_mutexattr_t  *mutexattr);
/****************************************************************
参数:1、设置互斥锁的变量(容器),通过传变量的地址
          2、设置互斥锁属性(通常设置为NULL表示默认方式)
返回值:返回值只有0
****************************************************************/
  • 加锁(阻塞)
int pthread_mutex_lock(pthread_mutex_t *mutex);
/****************************************************************
参数:指定要操作的互斥锁是哪个

返回值:成功返回0失败返回非0
****************************************************************/
  • 以非阻塞形式加锁(当执行加锁操作时,如果没有加锁成功,直接返回)
int pthread_mutex_trylock(pthread_mutex_t *mutex);
/****************************************************************
参数:指定要操作的互斥锁是哪个

返回值:成功返回0失败返回非0
****************************************************************/
  • 解锁
int pthread_mutex_unlock(pthread_mutex_t *mutex);
/****************************************************************
参数:指定要操作的互斥锁是哪个

返回值:成功返回0失败返回非0
****************************************************************/
  • 销毁锁,让互斥锁变量不再代表锁
int pthread_mutex_destroy(pthread_mutex_t *mutex);
/****************************************************************
参数:指定要操作的互斥锁是哪个

返回值:成功返回0失败返回非0
****************************************************************/
  • 池类算法
sched_yield()//出让调度器

在临界区中,为了不造成死锁,多注意临界区中的跳转(break、continue、goto、函数调用)语句和死循环语句

简单实例

/*****************************************************************
*   Copyright (C) 2019 Sangfor Ltd. All rights reserved.
*   
*   文件名称:thread_file.c
*   创 建 者:yinfei-hu
*   创建日期:2019-03-09   22:28:59
*   功能描述:文件:内容 1
*   20线程-》打开文件-》读数据-》往数据上+1,再写回去-》关闭-》退出
*
*****************************************************************/

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

#define THREADNUM 20
#define BUFSIZE 10

static int num = 0;

static pthread_mutex_t mut;
    
void *thread_fcun(void *arg){
    FILE *fp;
    char buf[BUFSIZE];
    
    fp = fopen("./hello.txt","r+");
    
    if(NULL == fp){
        perror("fopen()");
        pthread_exit(NULL);;
    }
    
    pthread_mutex_lock(&mut);//申请锁
    
    fgets(buf,BUFSIZE,fp);//读
    
    int num = atoi(buf);//转换

    num ++;//加1
    
    fseek(fp,0,SEEK_SET);//偏移
    
    fprintf(fp,"%d",num);//写
    
    fflush(fp);
    
    pthread_mutex_unlock(&mut);//解锁
    
    fclose(fp);
    
    pthread_exit(NULL);
}

int main(){
    int i,err;
    pthread_t tid[THREADNUM];

    pthread_mutex_init(&mut,NULL);//创建锁
    
    for(int i = 0;i < THREADNUM;i++){
        err = pthread_create(tid + i,NULL,thread_fcun,NULL);
        if(err){
            fprintf(stderr,"pthread_create():%s\n",strerror(err));
            exit(1);
        }
    }

    for(int i = 0;i < THREADNUM;i++){
        pthread_join(tid[i],NULL);
    }
    
    pthread_mutex_destroy(&mut);
    exit(0);
}

条件变量:pthread_cond_t

  • 初始化条件变量
 pthread_cond_init(条件变量指针,属性)
//初始化条件变量
//属性:NULL为默认属性
//使用宏初始化:pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
  • 销毁条件变量
ptherad_cond_destroy(条件变量指针)
  • 解锁等待通知,被唤醒后再申请锁
pthread_cond_wait(条件变量指针,互斥量指针)
  • 解锁等待指定时间,如果在指定时间内都没有收到唤醒通知,自动醒来申请锁,此时函数立马返回ETIMEDOUT
pthread_cond_timedwait(条件变量指针,互斥量指针,超时时间)
设置超时2秒的方式:

struct timespec ts;
ts.tv_sec = time(0) + 2; //获取当前系统时间+2
ts.tv_nsec = 0;          //此处设置纳秒为0
pthread_cond_timedwait(&cond,&mut,&ts);
  • 唤醒所有的等待线程
pthread_cond_broadcast(条件变量指针)
  • 唤醒任意一个等待的线程
pthread_cond_signal(条件变量指针)

上一篇:

下一篇: