IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 系统运维 -> Linux学习与总结(8) -> 正文阅读

[系统运维]Linux学习与总结(8)

互斥锁

互斥:是指散步在不同任务之间的若干程序片断,当某个任务运行其中一个程序片段时,其它任务就不能运行它们之中的任一程序片段,只能等到该任务运行完这个程序片段后才可以运行。
最基本的场景就是:一个公共资源同一时刻只能被一个进程或线程使用,多个进程或线程不能同时使用公共资源。

同步:是指散步在不同任务之间的若干程序片断,它们的运行必须严格按照规定的某种先后次序来运行,这种先后次序依赖于要完成的特定的任务。
最基本的场景就是:两个或两个以上的进程或线程在运行过程中协同步调,按预定的先后次序运行。比如 A 任务的运行依赖于 B 任务产生的数据。

互斥具有唯一性和排它性,但互斥并不限制任务的运行顺序,即任务是无序
同步的任务之间则有顺序关系

互斥锁Mutex
互斥锁(mutex),也叫互斥量,互斥锁是一种简单的加锁的方法来控制对共享资源的访问,互斥锁只有两种状态,即加锁( lock )和解锁( unlock )。

互斥锁的操作流程如下:

  1. 在访问共享资源后临界区域前,对互斥锁进行加锁。
  2. 在访问完成后释放互斥锁导上的锁。
  3. 对互斥锁进行加锁后,任何其他试图再次对互斥锁加锁的线程将会被阻塞,直到锁被释放。

互斥锁的数据类型是: pthread_mutex_t

安装:sudo apt-get install manpages-posix-dev

pthread_mutex_init 函数

#include <pthread.h>
?
int pthread_mutex_init(pthread_mutex_t *restrict mutex,
    const pthread_mutexattr_t *restrict attr);
功能:
    初始化一个互斥锁。
参数:
    mutex:互斥锁地址。类型是 pthread_mutex_t 。
    attr:设置互斥量的属性,通常可采用默认属性,即可将 attr 设为 NULL。
?
    可以使用宏 PTHREAD_MUTEX_INITIALIZER 静态初始化互斥锁,比如:
    pthread_mutex_t  mutex = PTHREAD_MUTEX_INITIALIZER;
?
这种方法等价于使用 NULL 指定的 attr 参数调用 pthread_mutex_init() 来完成动态初始化,不同之处在于 PTHREAD_MUTEX_INITIALIZER 宏不进行错误检查。
?
返回值:
    成功:0,成功申请的锁默认是打开的。
    失败:非 0 错误码

restrict,C语言中的一种类型限定符(Type Qualifiers),用于告诉编译器,对象已经被指针所引用,不能通过除该指针外所有其他直接或间接的方式修改该对象的内容

pthread_mutex_destroy函数

#include <pthread.h>

int pthread_mutex_destroy(pthread_mutex_t *mutex);
功能:
    销毁指定的一个互斥锁。互斥锁在使用完毕后,必须要对互斥锁进行销毁,以释放资源。
参数:
    mutex:互斥锁地址。
返回值:
    成功:0
    失败:非 0 错误码

pthread_mutex_lock函数

#include <pthread.h>
?
int pthread_mutex_lock(pthread_mutex_t *mutex);
功能:
    对互斥锁上锁,若互斥锁已经上锁,则调用者阻塞,直到互斥锁解锁后再上锁。
参数:
    mutex:互斥锁地址。
返回值:
    成功:0
    失败:非 0 错误码
?
int pthread_mutex_trylock(pthread_mutex_t *mutex);
?   调用该函数时,若互斥锁未加锁,则上锁,返回 0;
?   若互斥锁已加锁,则函数直接返回失败,即 EBUSY。

pthread_mutex_unlock函数

#include <pthread.h>
?
int pthread_mutex_unlock(pthread_mutex_t *mutex);
功能:
    对指定的互斥锁解锁。
参数:
    mutex:互斥锁地址。
返回值:
    成功:0
    失败:非0错误码
pthread_mutex_t mutex; //互斥锁
?
// 打印机
void printer(char *str)
{
    pthread_mutex_lock(&mutex); //上锁
    while (*str != '\0')
    {
        putchar(*str);
        fflush(stdout);
        str++;
        sleep(1);
    }
    printf("\n");
    pthread_mutex_unlock(&mutex); //解锁
}
?
// 线程一
void *thread_fun_1(void *arg)
{
    char *str = "hello";
    printer(str); //打印
}
?
// 线程二
void *thread_fun_2(void *arg)
{
    char *str = "world";
    printer(str); //打印
}
?
int main(void)
{
    pthread_t tid1, tid2;
?
    pthread_mutex_init(&mutex, NULL); //初始化互斥锁
?
    // 创建 2 个线程
    pthread_create(&tid1, NULL, thread_fun_1, NULL);
    pthread_create(&tid2, NULL, thread_fun_2, NULL);
?
    // 等待线程结束,回收其资源
    pthread_join(tid1, NULL);
    pthread_join(tid2, NULL);
?
    pthread_mutex_destroy(&mutex); //销毁互斥锁
?
    return 0;
}

死锁
死锁是指两个或两个以上的进程在执行过程中,由于竞争资源或者由于彼此通信而造成的一种阻塞的现象,若无外力作用,它们都将无法推进下去。
有进程p1,p2,都需要资源A,B,本来可以p1运行A --> p1运行B --> p2运行A --> p2运行B,但是顺序换了,p1运行A时p2运行B,容易发生第一种死锁。互相抢占资源。

死锁的必要条件

  1. 互斥条件
    某资源只能被一个进程使用,其他进程请求该资源时,只能等待,直到资源使用完毕后释放资源。
  2. 请求和保持条件
    程序已经保持了至少一个资源,但是又提出了新要求,而这个资源被其他进程占用,自己占用资源却保持不放。
  3. 不可抢占条件
    进程已获得的资源没有使用完,不能被抢占。
  4. 循环等待条件
    必然存在一个循环链

处理死锁的思路

  1. 预防死锁
    破坏死锁的四个必要条件中的一个或多个来预防死锁。
  2. 避免死锁
    和预防死锁的区别就是,在资源动态分配过程中,用某种方式防止系统进入不安全的状态。
  3. 检测死锁
    运行时出现死锁,能及时发现死锁,把程序解脱出来
  4. 解除死锁
    发生死锁后,解脱进程,通常撤销进程,回收资源,再分配给正处于阻塞状态的进程。

预防死锁的方法

  1. 破坏请求和保持条件
  2. 破坏不可抢占条件
  3. 破坏循环等待条件

读写锁

在对数据的读写操作中,更多的是读操作,写操作较少,例如对数据库数据的读写应用。为了满足当前能够允许多个读出,但只允许一个写入的需求,线程提供了读写锁来实现。

  1. 如果有其它线程读数据,则允许其它线程执行读操作,但不允许写操作。
  2. 如果有其它线程写数据,则其它线程都不允许读、写操作。

读写锁分为读锁和写锁,规则如下:

  1. 如果某线程申请了读锁,其它线程可以再申请读锁,但不能申请写锁。
  2. 如果某线程申请了写锁,其它线程不能申请读锁,也不能申请写锁。

POSIX 定义的读写锁的数据类型是: pthread_rwlock_t

宏在预处理进行替换,不做语法检查,语法检查发生在编译期

pthread_rwlock_init函数

#include <pthread.h>
?
int pthread_rwlock_init(pthread_rwlock_t *restrict rwlock,
    const pthread_rwlockattr_t *restrict attr);
功能:
    用来初始化 rwlock 所指向的读写锁。
?
参数:
    rwlock:指向要初始化的读写锁指针。
    attr:读写锁的属性指针。如果 attr 为 NULL 则会使用默认的属性初始化读写锁,否则使用指定的 attr 初始化读写锁。
?
    可以使用宏 PTHREAD_RWLOCK_INITIALIZER 静态初始化读写锁,比如:
    pthread_rwlock_t my_rwlock = PTHREAD_RWLOCK_INITIALIZER;
?
    这种方法等价于使用 NULL 指定的 attr 参数调用 pthread_rwlock_init() 来完成动态初始化,不同之处在于PTHREAD_RWLOCK_INITIALIZER 宏不进行错误检查。
?
返回值:
    成功:0,读写锁的状态将成为已初始化和已解锁。
    失败:非 0 错误码。
?

pthread_rwlock_destroy函数

#include <pthread.h>
?
int pthread_rwlock_destroy(pthread_rwlock_t *rwlock);
功能:
    用于销毁一个读写锁,并释放所有相关联的资源(所谓的所有指的是由 pthread_rwlock_init() 自动申请的资源) 。
参数:
    rwlock:读写锁指针。
返回值:
    成功:0
    失败:非 0 错误码

pthread_rwlock_rdlock函数

#include <pthread.h>
?
int pthread_rwlock_rdlock(pthread_rwlock_t *rwlock);
功能:
    以阻塞方式在读写锁上获取读锁(读锁定)。
    如果没有写者持有该锁,并且没有写者阻塞在该锁上,则调用线程会获取读锁。
    如果调用线程未获取读锁,则它将阻塞直到它获取了该锁。一个线程可以在一个读写锁上多次执行读锁定。
    线程可以成功调用 pthread_rwlock_rdlock() 函数 n 次,但是之后该线程必须调用 pthread_rwlock_unlock() 函数 n 次才能解除锁定。
参数:
    rwlock:读写锁指针。
返回值:
    成功:0
    失败:非 0 错误码
?
int pthread_rwlock_tryrdlock(pthread_rwlock_t *rwlock);
?   用于尝试以非阻塞的方式来在读写锁上获取读锁。
?   如果有任何的写者持有该锁或有写者阻塞在该读写锁上,则立即失败返回。

pthread_rwlock_wrlock函数

#include <pthread.h>
?
int pthread_rwlock_wrlock(pthread_rwlock_t *rwlock);
功能:
    在读写锁上获取写锁(写锁定)。
    如果没有写者持有该锁,并且没有写者读者持有该锁,则调用线程会获取写锁。
    如果调用线程未获取写锁,则它将阻塞直到它获取了该锁。
参数:
    rwlock:读写锁指针。
返回值:
    成功:0
    失败:非 0 错误码
?
int pthread_rwlock_trywrlock(pthread_rwlock_t *rwlock);
?   用于尝试以非阻塞的方式来在读写锁上获取写锁。
?   如果有任何的读者或写者持有该锁,则立即失败返回。
?

pthread_rwlock_unlock函数

#include <pthread.h>
?
int pthread_rwlock_unlock(pthread_rwlock_t *rwlock);
功能:
    无论是读锁或写锁,都可以通过此函数解锁。
参数:
    rwlock:读写锁指针。
返回值:
    成功:0
    失败:非 0 错误码

在此示例程序中,共创建了 4 个线程,其中两个线程用来写入数据,两个线程用来读取数据。当某个线程读操作时,其他线程允许读操作,却不允许写操作;当某个线程写操作时,其它线程都不允许读或写操作。

pthread_rwlock_t rwlock; //读写锁
int num = 1;
?
//读操作,其他线程允许读操作,却不允许写操作
void *fun1(void *arg)
{
    while (1)
    {
        pthread_rwlock_rdlock(&rwlock);
        printf("read num first===%d\n", num);
        pthread_rwlock_unlock(&rwlock);
        sleep(1);
    }
}
?
//读操作,其他线程允许读操作,却不允许写操作
void *fun2(void *arg)
{
    while (1)
    {
        pthread_rwlock_rdlock(&rwlock);
        printf("read num second===%d\n", num);
        pthread_rwlock_unlock(&rwlock);
        sleep(2);
    }
}
?
//写操作,其它线程都不允许读或写操作
void *fun3(void *arg)
{
    while (1)
    {
?
        pthread_rwlock_wrlock(&rwlock);
        num++;
        printf("write thread first\n");
        pthread_rwlock_unlock(&rwlock);
        sleep(2);
    }
}
?
//写操作,其它线程都不允许读或写操作
void *fun4(void *arg)
{
    while (1)
    {
?
        pthread_rwlock_wrlock(&rwlock);
        num++;
        printf("write thread second\n");
        pthread_rwlock_unlock(&rwlock);
        sleep(1);
    }
}
?
int main()
{
    pthread_t ptd1, ptd2, ptd3, ptd4;
?
    pthread_rwlock_init(&rwlock, NULL);//初始化一个读写锁
?
    //创建线程
    pthread_create(&ptd1, NULL, fun1, NULL);
    pthread_create(&ptd2, NULL, fun2, NULL);
    pthread_create(&ptd3, NULL, fun3, NULL);
    pthread_create(&ptd4, NULL, fun4, NULL);
?
    //等待线程结束,回收其资源
    pthread_join(ptd1, NULL);
    pthread_join(ptd2, NULL);
    pthread_join(ptd3, NULL);
    pthread_join(ptd4, NULL);
?
    pthread_rwlock_destroy(&rwlock);//销毁读写锁
?
    return 0;
}

条件变量

与互斥锁不同,条件变量是用来等待而不是用来上锁的,条件变量本身不是锁

条件变量用来自动阻塞一个线程,直到某特殊情况发生为止。通常条件变量和互斥锁同时使用

条件变量的两个动作:

  1. 条件不满, 阻塞线程
  2. 当条件满足, 通知阻塞的线程开始工作

条件变量的类型: pthread_cond_t

pthread_cond_init函数

#include <pthread.h>
?
int pthread_cond_init(pthread_cond_t *restrict cond,
    const pthread_condattr_t *restrict attr);
功能:
    初始化一个条件变量
参数:
    cond:指向要初始化的条件变量指针。
    attr:条件变量属性,通常为默认值,传NULL即可
        也可以使用静态初始化的方法,初始化条件变量:
        pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
返回值:
    成功:0
    失败:非0错误号

pthread_cond_destroy函数


#include <pthread.h>
?
int pthread_cond_destroy(pthread_cond_t *cond);
功能:
    销毁一个条件变量
参数:
    cond:指向要初始化的条件变量指针
返回值:
    成功:0
    失败:非0错误号

pthread_cond_wait函数

#include <pthread.h>
?
int pthread_cond_wait(pthread_cond_t *restrict cond,
    pthread_mutex_t *restrict mutex);
功能:
    阻塞等待一个条件变量
    a) 阻塞等待条件变量cond(参1)满足
    b) 释放已掌握的互斥锁(解锁互斥量)相当于pthread_mutex_unlock(&mutex);
            a) b) 两步为一个原子操作。
    c) 当被唤醒,pthread_cond_wait函数返回时,解除阻塞并重新申请获取互斥锁pthread_mutex_lock(&mutex);
?
参数:
    cond:指向要初始化的条件变量指针
    mutex:互斥锁
?
返回值:
    成功:0
    失败:非0错误号
?
int pthread_cond_timedwait(pthread_cond_t *restrict cond,
    pthread_mutex_t *restrict mutex,
    const struct
                           .*restrict abstime);
功能:
    限时等待一个条件变量
?
参数:
    cond:指向要初始化的条件变量指针
    mutex:互斥锁
    abstime:绝对时间
?
返回值:
    成功:0
    失败:非0错误号
?

abstime补充说明:

struct timespec {
    time_t tv_sec;      /* seconds */ // 秒
    long   tv_nsec; /* nanosecondes*/ // 纳秒
}
?
time_t cur = time(NULL);        //获取当前时间。
struct timespec t;              //定义timespec 结构体变量t
t.tv_sec = cur + 1;             // 定时1秒
pthread_cond_timedwait(&cond, &t);
?

pthread_cond_signal函数

#include <pthread.h>
?
int pthread_cond_signal(pthread_cond_t *cond);
功能:
    唤醒至少一个阻塞在条件变量上的线程
参数:
    cond:指向要初始化的条件变量指针
返回值:
    成功:0
    失败:非0错误号
?
int pthread_cond_broadcast(pthread_cond_t *cond);
功能:
    唤醒全部阻塞在条件变量上的线程
参数:
    cond:指向要初始化的条件变量指针
返回值:
    成功:0
    失败:非0错误号
?

在这里插入图片描述
在这里插入图片描述

生产者消费者条件变量模型
线程同步典型的案例即为生产者消费者模型,而借助条件变量来实现这一模型,是比较常见的一种方法。
假定有两个线程,一个模拟生产者行为,一个模拟消费者行为。两个线程同时操作一个共享资源(一般称之为汇聚),生产向其中添加产品,消费者从中消费掉产品。

// 节点结构
typedef struct node
{
    int data;
    struct node* next;
}Node;
?
// 永远指向链表头部的指针
Node* head = NULL;
?
// 线程同步 - 互斥锁
pthread_mutex_t mutex;
// 阻塞线程 - 条件变量类型的变量
pthread_cond_t cond;
?
// 生产者
void* producer(void* arg)
{
    while (1)
    {
        // 创建一个链表的节点
        Node* pnew = (Node*)malloc(sizeof(Node));
        // 节点的初始化
        pnew->data = rand() % 1000; // 0-999
?
        // 使用互斥锁保护共享数据
        pthread_mutex_lock(&mutex);
        // 指针域
        pnew->next = head;
        head = pnew;
        printf("====== produce: %lu, %d\n", pthread_self(), pnew->data);
        pthread_mutex_unlock(&mutex);
?
        // 通知阻塞的消费者线程,解除阻塞
        pthread_cond_signal(&cond);
?
        sleep(rand() % 3);
    }
    return NULL;
}
?
void* customer(void* arg)
{
    while (1)
    {
        pthread_mutex_lock(&mutex);
        // 判断链表是否为空
        if (head == NULL)
        {
            // 线程阻塞
            // 该函数会对互斥锁解锁
            pthread_cond_wait(&cond, &mutex);
            // 解除阻塞之后,对互斥锁做加锁操作
        }
        // 链表不为空 - 删掉一个节点 - 删除头结点
        Node* pdel = head;
        head = head->next;
        printf("------ customer: %lu, %d\n", pthread_self(), pdel->data);
        free(pdel);
        pthread_mutex_unlock(&mutex);
    }
    return NULL;
}
?
int main(int argc, const char* argv[])
{
    pthread_t p1, p2;
    // init
    pthread_mutex_init(&mutex, NULL);
    pthread_cond_init(&cond, NULL);
?
    // 创建生产者线程
    pthread_create(&p1, NULL, producer, NULL);
    // 创建消费者线程
    pthread_create(&p2, NULL, customer, NULL);
?
    // 阻塞回收子线程
    pthread_join(p1, NULL);
    pthread_join(p2, NULL);
?
    pthread_mutex_destroy(&mutex);
    pthread_cond_destroy(&cond);
?
    return 0;
}

goto语言用在内核跳转

条件变量的优缺点

  1. 相较于mutex而言,条件变量可以减少竞争。
  2. 如直接使用mutex,除了生产者、消费者之间要竞争互斥量以外,消费者之间也需要竞争互斥量,但如果汇聚(链表)中没有数据,消费者之间竞争互斥锁是无意义的。
  3. 有了条件变量机制以后,只有生产者完成生产,才会引起消费者之间的竞争。提高了程序效率。

信号量

信号量广泛用于进程或线程间的同步和互斥,信号量本质上是一个非负的整数计数器,它被用来控制对公共资源的访问。

编程时可根据操作信号量值的结果判断是否对公共资源具有访问的权限,当信号量值大于 0 时,则可以访问,否则将阻塞。

PV 原语是对信号量的操作,一次 P 操作使信号量减1,一次 V 操作使信号量加1。

信号量主要用于进程或线程间的同步和互斥这两种典型情况。

信号量数据类型为:sem_t

信号量互斥
在这里插入图片描述
信号量同步
在这里插入图片描述
sem_init函数

#include <semaphore.h>
?
int sem_init(sem_t *sem, int pshared, unsigned int value);
功能:
    创建一个信号量并初始化它的值。一个无名信号量在被使用前必须先初始化。
参数:
    sem:信号量的地址。
    pshared:等于 0,信号量在线程间共享(常用);不等于0,信号量在进程间共享。
    value:信号量的初始值。
返回值:
    成功:0
    失败: - 1
?

sem_destroy函数

#include <semaphore.h>
?
int sem_destroy(sem_t *sem);
功能:
    删除 sem 标识的信号量。
参数:
    sem:信号量地址。
返回值:
    成功:0
    失败: - 1

信号量P操作(减1)

#include <semaphore.h>
?
int sem_wait(sem_t *sem);
功能:
    将信号量的值减 1。操作前,先检查信号量(sem)的值是否为 0,若信号量为 0,此函数会阻塞,直到信号量大于 0 时才进行减 1 操作。
参数:
    sem:信号量的地址。
返回值:
    成功:0
    失败: - 1
?
int sem_trywait(sem_t *sem);
?   以非阻塞的方式来对信号量进行减 1 操作。
?   若操作前,信号量的值等于 0,则对信号量的操作失败,函数立即返回。
?
int sem_timedwait(sem_t *sem, const struct timespec *abs_timeout);
?   限时尝试将信号量的值减 1
?   abs_timeout:绝对时间
?

abs_timeout补充说明:


struct timespec {
    time_t tv_sec;      /* seconds */ // 秒
    long   tv_nsec; /* nanosecondes*/ // 纳秒
}
?
time_t cur = time(NULL);        //获取当前时间。
struct timespec t;              //定义timespec 结构体变量t
t.tv_sec = cur + 1;             // 定时1秒
sem_timedwait(&cond, &t);

信号量V操作(加1)

#include <semaphore.h>
?
int sem_post(sem_t *sem);
功能:
    将信号量的值加 1 并发出信号唤醒等待线程(sem_wait())。
参数:
    sem:信号量的地址。
返回值:
    成功:0
    失败:-1

获取信号量的值

#include <semaphore.h>
?
int sem_getvalue(sem_t *sem, int *sval);
功能:
    获取 sem 标识的信号量的值,保存在 sval 中。
参数:
    sem:信号量地址。
    sval:保存信号量值的地址。
返回值:
    成功:0
    失败:-1

打印机实现:

sem_t sem; //信号量
?
void printer(char *str)
{
    sem_wait(&sem);//减一
    while (*str)
    {
        putchar(*str);
        fflush(stdout);
        str++;
        sleep(1);
    }
    printf("\n");
?
    sem_post(&sem);//加一
}
?
void *thread_fun1(void *arg)
{
    char *str1 = "hello";
    printer(str1);
}
?
void *thread_fun2(void *arg)
{
    char *str2 = "world";
    printer(str2);
}
?
int main(void)
{
    pthread_t tid1, tid2;
?
    sem_init(&sem, 0, 1); //初始化信号量,初始值为 1
?
    //创建 2 个线程
    pthread_create(&tid1, NULL, thread_fun1, NULL);
    pthread_create(&tid2, NULL, thread_fun2, NULL);
?
    //等待线程结束,回收其资源
    pthread_join(tid1, NULL);
    pthread_join(tid2, NULL);
?
    sem_destroy(&sem); //销毁信号量
?
    return 0;
}

信号量实现生产者消费者模型

// 节点结构
typedef struct node
{
    int data;
    struct node* next;
}Node;
?
// 永远指向链表头部的指针
Node* head = NULL;
?
// 信号量变量
sem_t sem_producer;
sem_t sem_customer;
?
// 生产者
void* producer(void* arg)
{
    while (1)
    {
        // 创建一个链表的节点
        Node* pnew = (Node*)malloc(sizeof(Node));
        // 节点的初始化
        pnew->data = rand() % 1000; // 0-999
?
        // 申请资源
        sem_wait(&sem_producer);
        
        // 指针域
        pnew->next = head;
        head = pnew;
        printf("====== produce: %lu, %d\n", pthread_self(), pnew->data);
        pthread_mutex_unlock(&mutex);
?
        // 通知消费者线程消费
        sem_post(&sem_customer);
?
        sleep(rand() % 3);
    }
    return NULL;
}
?
void* customer(void* arg)
{
    while (1)
    {
    	// 节点为空就等待
        sem_wait(&sem_customer);
        
        // 链表不为空 - 删掉一个节点 - 删除头结点
        Node* pdel = head;
        head = head->next;
        printf("------ customer: %lu, %d\n", pthread_self(), pdel->data);
        free(pdel);
        
        sem_post(&sem_prodecer);
    }
    return NULL;
}
?
int main(int argc, const char* argv[])
{
    pthread_t p1, p2;
    // init,设置总信号量为5
    sem_init(&sem_prodecer, 0, 5);
    sem_init(&sem_customer, 0, 0);
?
    // 创建生产者线程
    pthread_create(&p1, NULL, producer, NULL);
    // 创建消费者线程
    pthread_create(&p2, NULL, customer, NULL);
?
    // 阻塞回收子线程
    pthread_join(p1, NULL);
    pthread_join(p2, NULL);
?
    sem_destory(&sem_prodecer);
    sem_destory(&sem_customer);
?
    return 0;
}

信号量实现多个生产者消费者模型

// 节点结构
typedef struct node
{
    int data;
    struct node* next;
}Node;
?
// 永远指向链表头部的指针
Node* head = NULL;
?
// 信号量变量
sem_t sem_producer;
sem_t sem_customer;
?
// 生产者
void* producer(void* arg)
{
    while (1)
    {
        // 创建一个链表的节点
        Node* pnew = (Node*)malloc(sizeof(Node));
        // 节点的初始化
        pnew->data = rand() % 1000; // 0-999
?
        // 申请资源
        sem_wait(&sem_producer);
        
        // 指针域
        pnew->next = head;
        head = pnew;
        printf("====== produce: %lu, %d\n", pthread_self(), pnew->data);
        pthread_mutex_unlock(&mutex);
?
        // 通知消费者线程消费
        sem_post(&sem_customer);
?
        sleep(rand() % 3);
    }
    return NULL;
}
?
void* customer(void* arg)
{
    while (1)
    {
    	// 节点为空就等待
        sem_wait(&sem_customer);
        
        // 链表不为空 - 删掉一个节点 - 删除头结点
        Node* pdel = head;
        head = head->next;
        printf("------ customer: %lu, %d\n", pthread_self(), pdel->data);
        free(pdel);
        
        sem_post(&sem_prodecer);
    }
    return NULL;
}
?
int main(int argc, const char* argv[])
{
    pthread_t tid[6];
	int i;
	
    // init,设置总信号量为10
    sem_init(&sem_prodecer, 0, 10);
    sem_init(&sem_customer, 0, 0);
?	
	for(i = 0; i < 6; i++)
	{
		if(i < 2)
		{ 
			pthread_create(&tid[i], NULL, producer, NULL);	
		}
		else
		{
			pthread_create(&tid[i], NULL, customer, NULL);
		}
	}
?
  
  	for(i = 0; i < 6; i++)
  	{
		pthread_join(tid[i], NULL);
	}
    
?
    sem_destory(&sem_prodecer);
    sem_destory(&sem_customer);
?
    return 0;
}

自旋锁

int pthread_spin_destroy(pthread_spinlock_t *lock);
int pthread_spin_init(pthread_spinlock_t *lock, int pshared);

哲学家就餐问题
在这里插入图片描述

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <string.h>
#include <pthread.h>
?
pthread_mutex_t mutex[5];
?
void* dine(void* arg)
{
    int num = (int)arg;
    int left, right;
?
    if(num < 4)
    {
        // 前4个人,右手拿自己筷子
        right = num;
        left = num+1;
    }
    else if(num == 4)
    {
        // 最后一个人,右手拿别人筷子
        right = 0;
        left = num;
    }
?
    // 吃饭
    while(1)
    {
        // 右手加锁
        pthread_mutex_lock(&mutex[right]);
        // 尝试抢左手筷子
        if(pthread_mutex_trylock(&mutex[left]) == 0)
        {
            // 吃面。。。
            printf("%c 正在吃面。。。。。。\n", num+'A');
            // 吃完放筷子
            pthread_mutex_unlock(&mutex[left]);
        }
        // 解锁
        pthread_mutex_unlock(&mutex[right]);
        sleep(rand()%5);
    }
}
?
int main(int argc, const char* argv[])
{
    pthread_t p[5];
?
    for(int i=0; i<5; ++i)
    {
        pthread_mutex_init(&mutex[i], NULL);
    }
?
    for(int i=0; i<5; ++i)
    {
        pthread_create(&p[i], NULL, dine, (void*)i);
    }
?
    for(int i=0; i<5; ++i)
    {
        pthread_join(p[i], NULL);
    }
    
    for(int i=0; i<5; ++i)
    {
        pthread_mutex_destroy(&mutex[i]);
    }
    
    return 0;
}
  系统运维 最新文章
配置小型公司网络WLAN基本业务(AC通过三层
如何在交付运维过程中建立风险底线意识,提
快速传输大文件,怎么通过网络传大文件给对
从游戏服务端角度分析移动同步(状态同步)
MySQL使用MyCat实现分库分表
如何用DWDM射频光纤技术实现200公里外的站点
国内顺畅下载k8s.gcr.io的镜像
自动化测试appium
ctfshow ssrf
Linux操作系统学习之实用指令(Centos7/8均
上一篇文章      下一篇文章      查看所有文章
加:2022-03-12 18:00:11  更:2022-03-12 18:01:12 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/16 1:54:40-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码