线程、互斥锁、无名信号量、条件变量

发布于:2024-10-16 ⋅ 阅读:(25) ⋅ 点赞:(0)

一、线程

1.1 pthread_cancel(线程的取消)

#include <pthread.h> -- 所需头文件
int pthread_cancel(pthread_t thread);
功能:取消线程
参数:
    thread:要取消的线程的线程id
返回值;成功返回0,失败返回错误码

线程能否被取消还取决于线程的取消状态和取消类型:
#include <pthread.h> -- 所需头文件
inthread_setcancelstate(nt state,int *olestate);

1、设置线程为取消状态,默认为此状态
pthread_setcancelstate(PTHREAD_CANCEL_ENABLE,NULL);
2、设置线程为不可取消的状态,设置之后,线程不会对其他线程发送的取消请求响应
pthread_setcancelstate(PTHREAD_CANCEL_DISABLE,NULL);

int pthread_setcanceltype(int type,int *oldtype);
pthread_setcanceltype(PTHREAD_CANCEL_DEFERROR,NULL);//默认为延时取消
//设置取消状态为立即取消,但是系统不保证可以立即取消
pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS,NULL);

取消点:线程可被取消的节点
本质就是调用某些函数的时候,线程会检查是否有取消请求并相应。
比如系统调用,从内核空间返回用户空间的时候会检查是否有取消请求并相应。

线程取消代码示例:

#include <my_head.h>
pthread_t tid1,tid2;
void *thread_func(void *arg){
    //设置线程为不可取消状态,线程无法被取消
    pthread_setcancelstate(PTHREAD_CANCEL_DISABLE,NULL);
    while(1){
        printf("111\n");
    }
}
int main(int argc,const char *argv[]){
    int ret = 0;
    ret = pthread_create(&tid1,NULL,thread_func,NULL);
    if(0 != ret){
        printf("pthread_create error:%s\n",strerror(ret));
        return -1;
    }
    sleep(3);
    pthread_cancel(tid1);
    while(1);
    return 0;
}

运行结果:手动停止。。

#include <my_head.h>
pthread_t tid1,tid2;
void *thread_func(void *arg){
    // //设置线程为不可取消状态,线程无法被取消
    // pthread_setcancelstate(PTHREAD_CANCEL_DISABLE,NULL);
    
    //立即取消,不保证成功
    pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS,NULL);
    int a = 0;
    while(1){
        a++;
        //printf("111\n");
    }
}
int main(int argc,const char *argv[]){
    int ret = 0;
    ret = pthread_create(&tid1,NULL,thread_func,NULL);
    if(0 != ret){
        printf("pthread_create error:%s\n",strerror(ret));
        return -1;
    }
    sleep(3);
    pthread_cancel(tid1);
    while(1);
    return 0;
}

运行结果:手动停止。。。

1.2 多线程访问同一变量问题

#include <my_head.h>
pthread_t tid1,tid2;//创建两个线程
int money = 0;//要访问的变量

void *thread_func1(void *arg){
    //张三向银行存钱
    int temp = 0;
    for(int i = 0;i < 1000000;i++){
        temp = money;
        temp = temp+1;
        money = temp;
    }
}
void *thread_func2(void *arg){
    //李四向银行存钱
    int temp = 0;
    for(int i = 0; i < 1000000;i++){
        temp = money;
        temp = temp+1;
        money = temp;
    }
}

int main(int argc,const char *argv[]){
    int ret = 0;
    ret = pthread_create(&tid1,NULL,thread_func1,NULL);
    if(0 != ret){
        printf("pthread_create error:%s\n",strerror(ret));
        return -1;
    }
    ret = pthread_create(&tid2,NULL,thread_func2,NULL);
    if(0 != ret){
        printf("pthread_create error:%s\n",strerror(ret));
        return -1;
    }
    pthread_join(tid1,NULL);
    pthread_join(tid2,NULL);
    printf("银行最终存了%d元\n",money);
    
    return 0;
}

运行结果:由于线程间运行方式是,时间片轮询,上下文切换。谁抢到谁就先运行会导致最终数据混乱。。。

1.3 互斥锁

1.3.1 互斥锁的概念

互斥锁是多线程对同一个变量访问的时候,同一时刻只能有一个线程对该全局变量进行访问操作。

在linux中,提供了一种实现互斥锁的操作的工具,叫做互斥锁。

在线程A获取到这把锁的时候,线程B是无法获取到锁的。

1.3.1 互斥锁的API

#include <pthread.h>
int pthread_mutex_init(pthread_mutex_t *mutex,const pthread_mutexattr_t *attr);
功能:动态初始化互斥锁
参数:
    mutex:互斥锁
    attr:互斥锁属性 -- 填NULL即可,使用默认属性
返回值:成功返回0,失败返回错误码

互斥锁的静态初始化
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;


#include <pthread.h>
int pthread_mutex_lock(pthread_mutex_t *mutex);
功能:获取互斥锁,如果所以京被其他程序获取,该线程会阻塞等待
参数:
    mutex:要获取的锁
返回值:成功返回0,失败返回错误码

int pthread_mutex_unlock(pthread_mutex_t *mutex);
功能:释放锁
参数:
    mutex:要释放的锁
返回值:成功返回0,失败返回错误码

int pthread_mutex_destory(pthread_mutex_t *mutex);
功能:销毁锁
参数:
    mutex:要销毁的锁
返回值:成功返回0,失败返回错误码

1.3.2 互斥锁的使用示例

#include <my_head.h>
pthread_t tid1,tid2;//创建两个线程
pthread_mutex_t mtx1 = PTHREAD_MUTEX_INITIALIZER;

int money = 0;//要访问的变量

void *thread_func1(void *arg){
    //张三向银行存钱
    int temp = 0;
    for(int i = 0;i < 1000000;i++){
        pthread_mutex_lock(&mtx1);//上锁
        temp = money;
        temp = temp+1;
        money = temp;
        pthread_mutex_unlock(&mtx1);//释放锁
    }
}
void *thread_func2(void *arg){
    //李四向银行存钱
    int temp = 0;
    for(int i = 0; i < 1000000;i++){
        pthread_mutex_lock(&mtx1);
        temp = money;
        temp = temp+1;
        money = temp;
        pthread_mutex_unlock(&mtx1);
    }
}

int main(int argc,const char *argv[]){
    
    int ret = 0;
    ret = pthread_create(&tid1,NULL,thread_func1,NULL);
    if(0 != ret){
        printf("pthread_create error:%s\n",strerror(ret));
        return -1;
    }
    ret = pthread_create(&tid2,NULL,thread_func2,NULL);
    if(0 != ret){
        printf("pthread_create error:%s\n",strerror(ret));
        return -1;
    }
    pthread_join(tid1,NULL);
    pthread_join(tid2,NULL);
    printf("银行最终存了%d元\n",money);
    //销毁锁
    pthread_mutex_destroy(&mtx1);

    return 0;
}

运行结果:

死锁问题:

死锁的概念:

        死锁就是当某个线程想要获取一把锁,但是由于这把锁被其他线程长时间占用不释放,导致该线程长时间阻塞,无法向下执行。

死锁产生的情况:

  1. 线程使用锁之后不释放
  2. 有两把锁AB,线程A获取了锁A,线程B获取了锁B。然后线程A想要获取锁B,线程B想要获取锁A,就会导致死锁。

思索地解决办法:

  1. 用完锁之后及时释放,不要长时间持有锁。
  2. 按顺序加锁
  3. 避免嵌套使用锁
  4. pthread_mutex_timedlock根据时间加锁
  5. 在全局变量中指定所的状态,在获取锁之前先判断锁的状态,如果锁已经被占用,就不要尝试获取。

1.4 无名信号量(线程同步)

1.4.1 同步的概念

线程指的是让线程按照既定的顺序执行。

1.4.2 无名信号量API分析

#include <semaphore.h> --所需头文件
int sem_init(sem_t *sem,int pshared,unsigned int value);
功能:初始化无名信号量
参数:
    sem:无名信号量
    pshared:
        0:用于线程间通信
        非0:用于进程间通信(亲缘关系进程间通信)
       value:信号量的初始值,大于等于0
初始化:成功返回0,失败返回-1,置位错误码

int sem_wait(sem_t *sem);
功能:获取信号量,让信号量的数值-1,如果信号量的数值已经是0,则阻塞线程
参数:
    sem:信号量
返回值:成功返回0,失败返回-1,置位错误码


int sem_post(sem_t *sem);
功能:释放信号量,让数值+1
参数:
    sem:信号量
返回值:成功返回0,失败返回-1,置位错误码

int sem_destory(sem_t *sem);
功能:销毁信号量
参数:
    sem:信号量
返回值:成功返回0,失败返回-1,置位错误码

无名信号量使用示例

#include <my_head.h>

pthread_t tid1,tid2;
pthread_mutex_t mtx1 = PTHREAD_MUTEX_INITIALIZER;
pthread_mutex_t mtx2 = PTHREAD_MUTEX_INITIALIZER;
sem_t sem1;
sem_t sem2;
int cars = 10;
//生产者
void *thread_func1(void *arg){
    while(1){
        sem_wait(&sem2);
        printf("生产者,生产一辆汽车\n");
        pthread_mutex_lock(&mtx1);//获取锁
        cars++;
        printf("现有%d辆汽车\n",cars);
        pthread_mutex_unlock(&mtx1);
        sem_post(&sem1);//释放信号量
    }
}
//消费者
void *thread_func2(void *arg){
    while(1){
        sem_wait(&sem1);
        printf("消费者,买了一辆汽车\n");
        cars--;
        pthread_mutex_lock(&mtx1);//获取锁
        printf("现有%d辆汽车\n",cars);
        pthread_mutex_unlock(&mtx1);
        sem_post(&sem2);//释放信号量
    }
}
int main(int argc,const char *argv[]){
    //信号量初始化
    sem_init(&sem1,0,10);
    sem_init(&sem2,0,0);
    int ret = 0;
    ret = pthread_create(&tid1,NULL,thread_func1,NULL);
    if(0 != ret){
        printf("pthread_create error:%s\n",strerror(ret));
        return -1;
    }
    ret = pthread_create(&tid2,NULL,thread_func2,NULL);
    if(0 != ret){
        printf("pthread_create error:%s\n",strerror(ret));
        return -1;
    }
    pthread_join(tid1,NULL);
    pthread_join(tid2,NULL);
    pthread_mutex_destroy(&mtx1);
    pthread_mutex_destroy(&mtx1);
    return 0;
}

运行结果:

1.5 条件变量(线程同步)

1.5.1 分析API

#include <pthread.h>
int pthread_cond_init(pthread_cond_t *cond,const pthread_condattr_t *att);
功能:初始化条件变量
参数:
    cond:条件变量
    attr:条件变量属性--传NULL使用默认属性
返回值:成功返回0,失败返回错误码
othread_cond_t cond = PTHREAD_COND_INITIALIZER;

int pthread_cond_wait(pthread_cond_t *cond,pthread_muntx_t *muntex);
功能:等待条件变量
参数:
    cond:要等待的条件变量
    mutex:互斥锁
返回值:成功返回0,失败返回错误码

1.先获取互斥锁;pthread_munex_lock
2.调用pthread_cond_wait函数
    01:将线程加入等待队列
    02:释放锁
    03:休眠
    04:获取锁
    05:退出休眠


int pthread_cond_signal(pthread_cond_t *cond);
功能:保证唤醒一个正在等待的cond条件变量通知的线程
参数:
    cond:条件变量
返回值:成功返回0,失败返回错误码

int pthread_cond_broadcast(pthread_cond_t *cond);
功能:唤醒所有正在等待的线程
参数:
    cond:条件变量
返回值:成功返回0,失败返回错误码


int pthread_cond_destory(pthread_cnd_t *cond);
功能:销毁条件变量
参数:
        cond:条件变量
返回值:成功返回0,失败返回错误码


1.5.2 条件变量使用示例

一个生产者多个消费者

#include <my_head.h>
pthread_t tid1,tid2;
pthread_cond_t cond;//定义条件变量
pthread_mutex_t mtx = PTHREAD_MUTEX_INITIALIZER;
int flag = 0;
//生产者
void *thread_func1(void *arg){
    while(1){
        pthread_mutex_lock(&mtx);
        if(0 != flag)
            pthread_cond_wait(&cond,&mtx);
        printf("我生产了一辆汽车\n");
        flag = 1;
        pthread_mutex_unlock(&mtx);
        pthread_cond_signal(&cond);//保证唤醒一个线程
        
    }
}
void *thread_func2(void *arg){
    while(1){
        pthread_mutex_lock(&mtx);
        if(0 == flag)
            pthread_cond_wait(&cond,&mtx);
        printf("消费者买了一辆汽车\n");
        flag = 0;
        pthread_cond_signal(&cond);
        pthread_mutex_unlock(&mtx);
    }
}
int main(int argc,const char *argv[]){
    //初始化条件变量
    pthread_cond_init(&cond,NULL);
    int ret = 0;
    ret = pthread_create(&tid1,NULL,thread_func1,NULL);
    if(0 != ret){
        printf("pthread_cretae error:%s\n",strerror(ret));
        return -1;
    }
        ret = pthread_create(&tid2,NULL,thread_func2,NULL);
    if(0 != ret){
        printf("pthread_cretae error:%s\n",strerror(ret));
        return -1;
    }

    pthread_join(tid1,NULL);
    //回收线程资源
    pthread_join(tid2,NULL);
    return 0;
}

运行结果:

总结:

        无名信号量1,适用于对限定的数量资源的同步访问操作。一般使用无名信号量做少量线程间的同步。

        条件变量是根据条件的变化去实现线程间同步操作。

        条件变量适用于大量线程间的同步操作。