Liunx高级系统设计9-线程间同步与互斥

news2024/9/30 3:33:55

同步与互斥的概念

互斥:同一时间,只能有一个任务(进程或线程)执行,谁先执行不确定。
同步:同一时间,只能有一个任务(进程或线程)执行,有顺序的执行。
同步 是特殊的互斥。

锁(互斥锁)

作用:又名互斥锁,让多个线程时,保证同时只能有一个线程执行任务

用于线程的互斥。
互斥锁是一种简单的加锁的方法来控制对共享资源的访问。
互斥锁只有两种状态 , 即加锁 ( lock ) 和解锁( unlock )。
操作原理:
        1)在访问共享资源后临界区域前,对互斥锁进行加锁。
        2)在访问完成后释放互斥锁导上的锁。
        3)对互斥锁进行加锁后,任何其他试图再次对互斥锁加锁的线程将会被阻塞,直到锁被释放。
注意 : 多个线程互斥锁要是同一个
互斥锁的数据类型是 :pthread_mutex_t

初始化

所需头文件
        #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 错误
#include <stdio.h>
#include <pthread.h>
#include <unistd.h>
int main(int argc, char const *argv[])
{
    //动态获取互斥锁,推荐
    pthread_mutex_t lock;
    pthread_mutex_init(&lock,NULL);
    //静态获取互斥锁,声明与赋值必须同时进程
        pthread_mutex_t lock02 = PTHREAD_MUTEX_INITIALIZER;
    pthread_mutex_destroy(&lock);
    pthread_mutex_destroy(&lock02);
    return 0;
}

销毁

作用 : 销毁互斥锁 , 互斥锁在使用完毕后,必须要对互斥锁进行销毁,以释放资源。
所需头文件
        #include <pthread.h>
函数
        int pthread_mutex_destroy(pthread_mutex_t *mutex);
参数:
        mutex:互斥锁地址。
返回值 :
        成功:0
        失败:非 0 错误码
#include <stdio.h>
#include <pthread.h>
#include <unistd.h>
int main(int argc, char const *argv[])
{
    //动态获取互斥锁,推荐
    pthread_mutex_t lock;
    pthread_mutex_init(&lock,NULL);
    //静态获取互斥锁
    pthread_mutex_t lock02 = PTHREAD_MUTEX_INITIALIZER;
    pthread_mutex_destroy(&lock);
    pthread_mutex_destroy(&lock02);
    return 0;
}

上锁

作用 : 对互斥锁上锁,若互斥锁已经上锁,则调用者阻塞,直到互斥锁解锁后再上锁。
所需头文件
        #include <pthread.h>
函数
        int pthread_mutex_lock(pthread_mutex_t *mutex);
参数:
        mutex:互斥锁地址。
返回值:
        成功:0
        失败:非 0 错误码

解锁

作用 : 对指定的互斥锁解锁。
所需头文件
        #include <pthread.h>
函数
i        nt pthread_mutex_unlock(pthread_mutex_t *mutex);
参数:
        mutex:互斥锁地址。
返回值:
        成功:0
        失败:非 0 错误码

死锁

概念 : 多个线程互相持有对方所需的锁资源
结果 : 程序无法向下运行 , 所以不会结束 , 但又不能执行代码
总结 : 避免死锁
#include <stdio.h>
#include <pthread.h>
#include <stdlib.h>
#include <unistd.h>
pthread_mutex_t lockA,lockB;
void *testA(void *argv)
{
    pthread_mutex_lock(&lockA);
    printf("线程%ld进入锁A中\n",pthread_self());
    sleep(1);
    pthread_mutex_lock(&lockB);
    printf("线程%ld进入锁B中\n",pthread_self());
    sleep(1);
    pthread_mutex_unlock(&lockA);
    pthread_mutex_unlock(&lockB);
    return NULL;
}
void *testB(void *argv)
{
    pthread_mutex_lock(&lockB);
    printf("线程%ld进入锁B中\n",pthread_self());
    sleep(1);
    pthread_mutex_lock(&lockA);
    printf("线程%ld进入锁A中\n",pthread_self());
    sleep(1);
    pthread_mutex_unlock(&lockA);
    pthread_mutex_unlock(&lockB);
    return NULL;
}
int main(int argc, char const *argv[])
{
    pthread_mutex_init(&lockA,NULL);
    pthread_mutex_init(&lockB,NULL);
    pthread_t t1,t2;
    pthread_create(&t1,NULL,testA,NULL);
    pthread_create(&t2,NULL,testB,NULL);
    pthread_join(t1,NULL);
    pthread_join(t2,NULL);
    pthread_mutex_destroy(&lockB);
    pthread_mutex_destroy(&lockA);
    printf("主线程OVER\n");
    return 0;
}

读写锁

        一个特殊的锁
        含有读写两种互斥锁
        其中读读不互斥, 读写互斥 , 写写互斥
        在使用多个线程对同一个数据进行读写时建议使用
        读写锁的数据类型是: pthread_rwlock_t
经验 :
        如果只有两个线程, 一个读 , 一个写 , 此时没必要使用读写锁 , 普通的互斥锁也是可以 的

初始化

作用:用来初始化 rwlock 所指向的读写锁。

所需头文件
        #include <pthread.h>
函数
        int pthread_rwlock_init(pthread_rwlock_t *restrict rwlock,
                const pthread_rwlockattr_t *restrict attr);
参数:
        rwlock:指向要初始化的读写锁指针。
        attr:读写锁的属性指针。如果 attr NULL 则会使用默认的属性初始化读写 锁,否则使用指定的 attr 初始化读写锁。
        可以使用宏PTHREAD_RWLOCK_INITIALIZER 静态初始化互斥锁 , 比如:
        pthread_rwlock_t rwlock = PTHREAD_RWLOCK_INITIALIZER;
返回值:
        成功:0 ,读写锁的状态将成为已初始化和已解锁。
        失败:非 0 错误码
#include <stdio.h>
#include <unistd.h>
#include <pthread.h>
#include <stdlib.h>
int main(int argc, char const *argv[])
{
    //动态初始化,推荐
    pthread_rwlock_t rwlock01;
    pthread_rwlock_init(&rwlock01,NULL);
    //静态初始化,不建议使用,声明与复制必须同时进行
    pthread_rwlock_t rwlock02 = PTHREAD_RWLOCK_INITIALIZER;
    return 0;
}
//注意:vscode编写时不会提示,需要手动编写

销毁

作用:用于销毁一个读写锁,并释放所有相关联的资源(所谓的所有指的是pthread_rwlock_init() 自动申请的资源)

所需头文件
        #include <pthread.h>
函数
        int pthread_rwlock_destroy(pthread_rwlock_t *rwlock);
参数:
        rwlock:读写锁指针。
返回值:
        成功:0
        失败:非 0 错误码
#include <stdio.h>
#include <unistd.h>
#include <pthread.h>
#include <stdlib.h>
int main(int argc, char const *argv[])
{
    //动态初始化,推荐
    pthread_rwlock_t rwlock01;
    pthread_rwlock_init(&rwlock01,NULL);
    //静态初始化,不建议使用,声明与复制必须同时进行
    pthread_rwlock_t rwlock02 = PTHREAD_RWLOCK_INITIALIZER;
    pthread_rwlock_destroy(&rwlock01);
    pthread_rwlock_destroy(&rwlock02);
    return 0;
}

申请读锁

        以阻塞方式在读写锁上获取读锁(读锁定)。
        如果没有写者持有该锁,并且没有写者阻塞在该锁上,则调用线程会获取读锁。
        如果调用线程未获取读锁,则它将阻塞直到它获取了该锁。一个线程可以在一个读写锁上多次执行读锁定。
        线程可以成功调用 pthread_rwlock_rdlock() 函数 n 次,但是之后该线程必须调用
pthread_rwlock_unlock() 函数 n 次才能解除锁定。

所需头文件
        #include <pthread.h>
函数
        int pthread_rwlock_rdlock(pthread_rwlock_t *rwlock);
参数:
        rwlock:读写锁指针。
返回值:
        成功:0
        失败:非 0 错误码
int pthread_rwlock_tryrdlock(pthread_rwlock_t *rwlock);
参数:
        rwlock:读写锁指针。
        用于尝试以非阻塞的方式来在读写锁上获取读锁。
        如果有任何的写者持有该锁或有写者阻塞在该读写锁上,则立即失败返回。

申请写锁

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

释放读写锁

作用:无论是读锁或写锁,都可以通过此函数解锁

所需头文件
        #include <pthread.h>
函数
        int pthread_rwlock_unlock(pthread_rwlock_t *rwlock);
参数:
        rwlock:读写锁指针。
返回值:
        成功:0
        失败:非 0 错误码
#include <stdio.h>
#include <unistd.h>
#include <pthread.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
pthread_rwlock_t rwlock;
//多线程公共读写数据
int num = 0;
void *writeNum(void *x)
{
    pthread_rwlock_wrlock(&rwlock);
    sleep(2);
    num = rand()%100;
    printf("线程%ld写入后num=%d\n",pthread_self(),num);
    pthread_rwlock_unlock(&rwlock);
    return NULL;
}
void *readNum(void *argv)
{
    pthread_rwlock_rdlock(&rwlock);
    sleep(2);
    printf("线程%ld读取到的num=%d\n",pthread_self(),num);
    pthread_rwlock_unlock(&rwlock);
    return NULL;
}
void closeThread(pthread_t ps[],int len)
{
    int i;
    for (i = 0; i < len; i++)
    {
        pthread_t t = ps[i];
        pthread_join(t,NULL);
    }
}
int main(int argc, char const *argv[])
{
    //初始化读写锁
    pthread_rwlock_init(&rwlock,NULL);
    //设置随机数种子
    srand(time(NULL));
    //声明3个线程写
    pthread_t tw[3];
    for(int i = 10; i < 13; i++)
    {
        pthread_create(&tw[i-10],NULL,writeNum,NULL);
    }
    //声明10个线程读
    pthread_t tr[10];
    for(int i = 0; i < 10; i++)
    {
        pthread_create(&tr[i],NULL,readNum,NULL);
    }
    int wlen = sizeof(tw)/sizeof(pthread_t);
    closeThread(tw,wlen);
    int rlen = sizeof(tw)/sizeof(pthread_t);
    closeThread(tr,rlen);
    pthread_rwlock_destroy(&rwlock);
    return 0;
}

条件变量

        与互斥锁不同,条件变量是用来等待而不是用来上锁的,条件变量本身不是锁!
        条件变量用来自动阻塞一个线程,直到某特殊情况发生为止。通常条件变量和互斥锁同时使用。
条件变量的两个动作:
        条件不满足, 阻塞线程
        当条件满足, 通知阻塞的线程开始工作
条件变量的类型 : pthread_cond_t

初始化

所需头文件
        #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 错误号

释放

所需头文件
        #include <pthread.h>
函数
        int pthread_cond_destroy(pthread_cond_t *cond);
参数:
        cond:指向要初始化的条件变量指针
返回值:
        成功:0
        失败:非 0 错误

等待条件满足

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

唤醒等待

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

案例1:条件变量基本演示

#include <stdio.h>
#include <pthread.h>
#include <unistd.h>
//声明互斥锁
pthread_mutex_t lock;
//声明条件变量
pthread_cond_t cond;
void *test01()
{
    pthread_mutex_lock(&lock);
    printf("线程%ld陷入休眠\n",pthread_self());
    pthread_cond_wait(&cond,&lock);
    printf("线程%ld被唤醒\n",pthread_self());
    pthread_mutex_unlock(&lock);
}
int main(int argc, char const *argv[])
{
    //声明线程
    pthread_t t01,t02;
    //初始化互斥锁
    pthread_mutex_init(&lock,NULL);
    //初始化条件变量
    pthread_cond_init(&cond,NULL);
    //创建线程
    pthread_create(&t01,NULL,test01,NULL);
    pthread_create(&t02,NULL,test01,NULL);
    sleep(5);
    //随机唤醒一个
    //pthread_cond_signal(&cond);
    //唤醒所有
    pthread_cond_broadcast(&cond);
    //销毁显示
    pthread_join(t01,NULL);
    pthread_join(t02,NULL);
    //释放互斥锁
    pthread_mutex_destroy(&lock);
    //释放条件变量
    pthread_cond_destroy(&cond);
    return 0;
}

案例2:生产者与消费者模式

#include <stdio.h>
#include <unistd.h>
#include <pthread.h>
#include <string.h>
#include <time.h>
#include <stdlib.h>
//声明互斥锁
pthread_mutex_t lock;
//声明条件变量
pthread_cond_t cond;
//声明记录库存数量的变量
int num = 0;
//声明生产的方法
void *produce(void *argv);
//声明销售的方法
void *sale(void *argv);
//声明释放线程的方法
void closeThread(pthread_t ts[],int len)
{
    for(int i = 0; i < len; i++)
    {
        pthread_join(ts[i],NULL);
    }
}
int main(int argc, char const *argv[])
{
    srand(time(NULL));
    //初始化互斥锁
    pthread_mutex_init(&lock,NULL);
    //初始化条件变量
    pthread_cond_init(&cond,NULL);
    //声明生产者线程组
    pthread_t ps[3];
    //声明销售者线程组
    pthread_t ss[5];
    //创建线程并执行
    int i;
    for(i = 0; i < 3; i++)
    {
        pthread_create(&ps[i],NULL,produce,NULL);
    }
    for(int i = 0; i < 5; i++)
    {
        pthread_create(&ps[i],NULL,sale,NULL);
    }
    //释放生产者线程
    int plen = sizeof(ps)/sizeof(pthread_t);
    closeThread(ps,plen);
    //释放消费者线程
    int slen = sizeof(ss)/sizeof(pthread_t);
    closeThread(ss,slen);
    //释放互斥锁
    pthread_mutex_destroy(&lock);
    //释放条件变量
    pthread_cond_destroy(&cond);
    return 0;
}
void *produce(void *argv)
{
    while(1){
        pthread_mutex_lock(&lock);
        while(num >= 10)
        {
            printf("库存已满,线程%ld停止生产\n",pthread_self());
            pthread_cond_wait(&cond,&lock);
        }
        num++;
        printf("线程%ld生产了一个商品,当前库存数量为:%d\n",pthread_self(),num);
        pthread_cond_broadcast(&cond);
        pthread_mutex_unlock(&lock);
        sleep(1);
    }
    return NULL;
}
void *sale(void *argv)
{
    while(1)
    {
        pthread_mutex_lock(&lock);
        while(num <= 0)
        {
            printf("库存为0,线程%ld停止销售\n",pthread_self());
            pthread_cond_wait(&cond,&lock);
        }
        num--;
        printf("线程%ld销售了一个商品,当前库存数量为:%d\n",pthread_self(),num);
        pthread_cond_broadcast(&cond);
        pthread_mutex_unlock(&lock);
        int t = rand()%5;
        sleep(t);
    }
}

信号量

        信号量广泛用于进程或线程间的同步和互斥, 信号量本质上是一个非负的整数计数器 , 它 被用来控制对公共资源的访问
        当信号量值大于0 时,则可以访问 , 否则将阻塞 .
        PV 原语是对信号量的操作 , 一次 P 操作使信号量减1,一次 V 操作使信号量加 1.
信号量数据类型为: sem_t

初始化信号量

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

p操作-信号量-1

所需头文件
        #include <semaphore.h>
函数
        int sem_wait(sem_t *sem);
功能 :
        将信号量减一, 如果信号量的值为 0 则阻塞 , 大于 0 可以减一
参数 :
        信号量的地址
返回值 :
        成功返回 0
        失败返回 -1
所需头文件
        #include <semaphore.h>
函数
        int sem_trywait(sem_t *sem);
功能 :
        尝试将信号量减一, 如果信号量的值为 0 不阻塞 , 立即返回 , 大于 0 可以减一
参数 :
        信号量的地址
返回值 :
        成功返回 0
        失败返回 -1

v操作-信号量+1

所需头文件
        #include <semaphore.h>
函数
        int sem_post(sem_t *sem);
功能 :
        将信号量加一
参数 :
        信号量的地址
返回值 :
        成功返回 0
        失败返回-1

销毁信号量

所需头文件
        #include <semaphore.h>
函数
        int sem_destroy(sem_t *sem);
功能 :
        销毁信号量
参数 :
        信号量的地址
返回值 :
        成功返回 0
        失败返回 -1

线程间

信号量完成互斥

        不管有多少个任务,只要是互斥,只要一个信号量,并且初始化1.
        当任务x 开始时 , 让信号量进行 p 操作 , 此时信号量为 0, 其他任务此时就会被阻塞 , 当任务 x 结束时让任务, 让信号量 v 操作 , 这样别的任务就可以执行了
#include <stdio.h>
#include <semaphore.h>
#include <pthread.h>
#include <unistd.h>
sem_t sem;
void *printstr(void *argv)
{
    sem_wait(&sem);
    char * str = (char *)argv;
    int i = 0;
    while(str[i] != '\0')
    {
        printf("%c\n",str[i]);
        i++;
        sleep(1);
    }
    sem_post(&sem);
    return NULL;
}
int main(int argc, char const *argv[])
{
    //初始化信号量,0表示线程 1初始值
    sem_init(&sem,0,1);
    pthread_t t01,t02,t03;
    pthread_create(&t01,NULL,printstr,"HELLO");
    pthread_create(&t02,NULL,printstr,"c++");
    pthread_create(&t03,NULL,printstr,"Java");
    pthread_join(t01,NULL);
    pthread_join(t02,NULL);
    pthread_join(t03,NULL);
    //释放信号量
    sem_destroy(&sem);
    return 0;
}

信号量完成同步

        有几个任务就需要有几个信号量,先执行为任务的信号初始化为1 ,其他信号量初始化为0
        所有任务P(-1) 自己的信号, V(+1) 下一个任务的信号量。
#include <stdio.h>
#include <pthread.h>
#include <semaphore.h>
#include <unistd.h>
sem_t s01,s02,s03;
void *print01(void *argv)
{
    sem_wait(&s01);
    printf("线程1输入\n");
    sem_post(&s02);
    return NULL;
}
void *print02(void *argv)
{
    sem_wait(&s02);
    printf("线程2输入\n");
    sem_post(&s03);
    return NULL;
}
void *print03(void *argv)
{
    sem_wait(&s03);
    printf("线程3输入\n");
    sem_post(&s01);
    return NULL;
}
int main(int argc, char const *argv[])
{
    sem_init(&s01,0,1);
    sem_init(&s02,0,0);
    sem_init(&s03,0,0);
    pthread_t t01,t02,t03;
    pthread_create(&t01,NULL,print01,NULL);
    pthread_create(&t02,NULL,print02,NULL);
    pthread_create(&t03,NULL,print03,NULL);
    pthread_join(t01,NULL);
    pthread_join(t02,NULL);
    pthread_join(t03,NULL);
    sem_destroy(&s01);
    sem_destroy(&s02);
    sem_destroy(&s03);
    return 0;
}

进程间

无名信号量

无名信号量用于 有血缘关系进程间的同步互斥。
mmap 创建无名信号量 ( 也可以通过别的方式 )
        #include <sys/mman.h>
        #include <semaphore.h>
                //MAP_ANONYMOUS匿名映射( 映射没有任何文件支持;其内容被初始化为零。 ) -1 不需要文件描述符
                sem_t *sem = mmap(NULL,sizeof(sem_t), PROT_READ|PROT_WRITE,
        MAP_SHARED|MAP_ANONYMOUS, -1, 0);
有血缘进程的互斥 : 案例 1
#include <stdio.h>
#include <unistd.h>
#include <semaphore.h> //信号量
#include <sys/mman.h> //mmap
#include <sys/wait.h> //wait
void print_string(void *str)
{
    char *p = (char *)str;
    int i = 0;
    while (p[i] != '\0')
    {
        printf("%c", p[i++]);
        fflush(stdout);
        sleep(1);
    }
}
int main(int argc, char const *argv[])
{
    //创建无名信号量
    // MAP_ANONYMOUS匿名映射 -1不需要文件描述符
    sem_t *sem = mmap(NULL, sizeof(sem_t), PROT_READ | PROT_WRITE,
    MAP_SHARED | MAP_ANONYMOUS, -1, 0);
    //初始化信号量 1表示作用于进程 1初始值
    sem_init(sem, 1, 1);
    pid_t pid = fork();
    if (pid == 0) //子进程
    {
        // p 操作
        sem_wait(sem);
        print_string("ni hao");
        // V 操作
        sem_post(sem);
        _exit(-1);
    }
    else if (pid > 0) //父进程
    {
        // p 操作
        sem_wait(sem);
        print_string("hello world");
        // V 操作
        sem_post(sem);
        wait(NULL);
    }
    //销毁信号量
    sem_destroy(sem);
    return 0;
}
有血缘进程间互斥 : 案例 2
#include <stdio.h>
#include <stdlib.h>
#include <semaphore.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/mman.h>
void printStr(char *str)
{
    int i = 0;
    while(str[i] != '\0')
    {
        printf("%c\n",str[i]);
        fflush(stdout);
        i++;
        sleep(1);
    }
    printf("\n");
}
int main(int argc, char const *argv[])
{
    //通过mmap(磁盘映射)创建有缘信号量
    sem_t *sem = (sem_t *)mmap(NULL,//映射区域地址,给NULL内核会自己选择合适的区域
    sizeof(sem_t),//映射区域大学
    PROT_READ | PROT_WRITE,//权限:可读,可写
    MAP_SHARED | MAP_ANONYMOUS,//标志位:共享的 | 映射没有任何文件支持;其内容被初始化为零
    -1,//文件标识符,当标志位有MAP_ANONYMOUS,文件标识符必须为-1
    0);//偏移量
        //初始化信号量
    //1参:要初始化的信号量指针
    //2参:0线程间共享,非0进程间共享
    //3参:信号量初始值
    sem_init(sem,1,1);
    int i = 0;
    for (i = 0; i < 2; i++)
    {
        pid_t pid = fork();
        if (pid == 0)
        {
            printf("进程%d被创建了\n",getpid());
            break;
        }
    }
    if (i == 0)
    {
        //子进程1
        //p操作,信号量-1
        sem_wait(sem);
        printStr("hello");
        sem_post(sem);
        _exit(-1);
    }
    else if(i == 1)
    {
        //子进程2
        sem_wait(sem);
        printStr("c++");
        sem_post(sem);
        _exit(-1);
    }
    else if(i == 2)
    {
        //父进程
        while (1)
        {
            //-1,等待任意子进程结束回收,
            //WNOHANG:不阻塞
            //返回值:被回收的子进程id
            pid_t pid = waitpid(-1,NULL,WNOHANG);
            if (pid > 1)
            {
                printf("进程%d,被回收了\n",pid);
            }
            else if (pid == 0)
            {
                //当pid为0说明当前并没有回收到子进程,还有子进程在运行
                    continue;
            }
            else if(pid < 0)
            {
                //当pid小于0说明当前父进程中已经没有子进程了
                break;
            }
        }
        //销毁信号量
        sem_destroy(sem);
    }
    getchar();
    return 0;
}
有血缘进程间同步
#include <stdio.h>
#include <unistd.h>
#include <semaphore.h> //信号量
#include <sys/mman.h> //mmap
#include <sys/wait.h> //wait
void print_string(void *str)
{
    char *p = (char *)str;
    int i = 0;
    while (p[i] != '\0')
    {
        printf("%c", p[i++]);
        fflush(stdout);
        sleep(1);
    }
}
int main(int argc, char const *argv[])
{
    //创建无名信号量
    // MAP_ANONYMOUS匿名映射 -1不需要文件描述符
    sem_t *sem1 = (sem_t *)mmap(NULL, sizeof(sem_t), PROT_READ | PROT_WRITE,
    MAP_SHARED | MAP_ANONYMOUS, -1, 0);
    sem_t *sem2 = (sem_t *)mmap(NULL, sizeof(sem_t), PROT_READ | PROT_WRITE,
    MAP_SHARED | MAP_ANONYMOUS, -1, 0);
    //初始化信号量 1表示作用于进程 1初始值
    sem_init(sem1, 1, 1);
    sem_init(sem2, 1, 0);
    pid_t pid = fork();
    if (pid == 0) //子进程
    {
        // p 操作
        sem_wait(sem1);
        print_string("ni hao");
        // V 操作
        sem_post(sem2);
        _exit(-1);
    }
    else if (pid > 0) //父进程
    {
        // p 操作
        sem_wait(sem2);
        print_string("hello world");
        // V 操作
        sem_post(sem1);
        wait(NULL);
    }
        //销毁信号量
    sem_destroy(sem1);
    sem_destroy(sem2);
    return 0;
}

有名信号量

有名信号量用于无血缘关系进程间的同步互斥。
所需头文件
        #include <fcntl.h> /* For O_* constants */
        #include <sys/stat.h> /* For mode constants */
        #include <semaphore.h>
函数
        //信号量存在
        sem_t *sem_open(const char *name, int oflag);
        //信号量不存在
        sem_t *sem_open(const char *name, int oflag,mode_t mode, unsigned int value);
        Link with -pthread.
        功能:创建一个有名信号量。
参数:
        name:信号量的标识符 , 建议以 / 开始 ( 存储在 /dev/shm 目录下 )
        oflag:和 open 函数的 flag 一致
        mode:磁盘权限 0666
        value:信号量的初始值
返回值:
        成功就是信号量的地址
        失败为NULL
        #include <semaphore.h>
        int sem_close(sem_t *sem);
        Link with -pthread
        功能: 关闭信号量
参数 :
        信号量地址
        #include <unistd.h>
        int unlink(const char *pathname);
        功能: 删除文件
参数 :
        文件地址
返回值 :
        成功返回 0
        失败返回-1

无血缘进程互斥

//18_codeA.c
#include <stdio.h>
#include <unistd.h>
#include <semaphore.h>
#include <fcntl.h>
#include <sys/stat.h>
void printStr(char *str)
{
    int i = 0;
    while(str[i] != '\0')
    {
        printf("%c\n",str[i]);
        i++;
        sleep(1);
    }
}
int main(int argc, char const *argv[])
{
    sem_t *sem = sem_open("sem",O_RDWR | O_CREAT,0666,1);
    sem_wait(sem);
    printStr("hello 123");
    sem_post(sem);
    sem_close(sem);
    sem_destroy(sem);
    return 0;
}
//18_codeB.c
#include <stdio.h>
#include <unistd.h>
#include <semaphore.h>
#include <fcntl.h>
#include <sys/stat.h>
void printStr(char *str)
{
    int i = 0;
    while(str[i] != '\0')
    {
        printf("%c\n",str[i]);
        i++;
        sleep(1);
    }
}
int main(int argc, char const *argv[])
{
    sem_t *sem = sem_open("sem",O_RDWR | O_CREAT,0666,1);
    sem_wait(sem);
    printStr("Hi C++");
    sem_post(sem);
    sem_close(sem);
    sem_destroy(sem);
    return 0;
}

有血缘进程同步

//19_codeA.c
#include <fcntl.h> /* For O_* constants */
#include <sys/stat.h> /* For mode constants */
#include <semaphore.h>
#include <stdio.h>
#include <unistd.h>
void print_string(void *str)
{
    char *p = (char *)str;
    int i = 0;
    while (p[i] != '\0')
    {
        printf("%c", p[i++]);
        fflush(stdout);
        sleep(1);
    }
}
int main(int argc, char const *argv[])
{
    //创建一个有名信号量
    sem_t *sem1 = sem_open("sem1", O_RDWR | O_CREAT, 0666, 1);
    sem_t *sem2 = sem_open("sem2", O_RDWR | O_CREAT, 0666, 0);
    // P 操作
    sem_wait(sem1);
    print_string("nihao xian");
    // V 操作
    sem_post(sem2);
    //关闭信号量
    sem_close(sem1);
    sem_close(sem2);
    //销毁信号
    sem_destroy(sem1);
    sem_destroy(sem2);
    return 0;
}
//19_codeB.c
#include <fcntl.h> /* For O_* constants */
#include <sys/stat.h> /* For mode constants */
#include <semaphore.h>
#include <stdio.h>
#include <unistd.h>
void print_string(void *str)
{
    char *p = (char *)str;
    int i = 0;
    while (p[i] != '\0')
    {
        printf("%c", p[i++]);
        fflush(stdout);
        sleep(1);
    }
}
int main(int argc, char const *argv[])
{
    //创建一个有名信号量
    sem_t *sem1 = sem_open("sem1", O_RDWR | O_CREAT, 0666, 1);
    sem_t *sem2 = sem_open("sem2", O_RDWR | O_CREAT, 0666, 0);
    // P 操作
    sem_wait(sem2);
    print_string("hello world");
    // V 操作
    sem_post(sem1);
        //关闭信号量
    sem_close(sem1);
    sem_close(sem2);
    //销毁信号
    sem_destroy(sem1);
    sem_destroy(sem2);
    return 0;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1309259.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

CSS的基本选择器及高级选择器(附详细示例以及效果图)

Hi i,m JinXiang ⭐ 前言 ⭐ 本篇文章主要介绍HTML中CSS的基础选择及高级选择器&#xff08;详解&#xff09;以及部分理论知识 &#x1f349;欢迎点赞 &#x1f44d; 收藏 ⭐留言评论 &#x1f4dd;私信必回哟&#x1f601; &#x1f349;博主收将持续更新学习记录获&#xf…

螺丝厂家:什么是钣金螺丝?

金属板由常见的金属或合金&#xff08;钢、铜、镍、锡、钛等&#xff09;组成&#xff0c;比木材更硬、更坚固。因此&#xff0c;它需要使用特殊的螺钉。您通常无法将传统螺钉拧入钣金中。值得庆幸的是&#xff0c;有专为钣金设计的特殊类型的螺钉。被称为钣金螺钉&#xff0c;…

对多个 App 设计工具组件使用一个回调

当要在App 中提供多种方法来执行某个操作时&#xff0c;在组件间共享回调非常有用。例如&#xff0c;当用户点击按钮或在编辑字段中按下 Enter 键时&#xff0c;App 可以用同样的方式响应。 共享回调的示例 此示例说明如何创建一个 App&#xff0c;其中包含共享一个回调的两个…

运筹优化 | 模拟退火求解旅行商问题 | Python实现

"""模拟退火旅行商""" import random import numpy as np import math import time import matplotlib.pyplot as plt plt.rcParams[font.sans-serif] [SimHei] plt.rcParams[axes.unicode_minus] False location np.loadtxt(city_location.t…

蓝牙插座_风扇_灯

项目需求 通过蓝牙模块&#xff0c;实现手机控制蓝牙插座 / 风扇 / 灯。 本质&#xff1a; 1. 采用蓝牙的透传功能&#xff1b; 2. 控制 IO 口的输出。 HC01_TX -- RX1 HC01_RX -- TX1 项目实现 1、串口非中断法 2. 串口中断法

【UML】第4篇 UML公共机制(补扩展机制)

目录 一、扩展机制 1.1 构造型 1.2 标记值&#xff08;Tagged Value&#xff09; 1.3 约束&#xff08;Constraint&#xff09; 上节扩展机制没有讲完&#xff0c;如上图。 一、扩展机制 1.1 构造型 UML中的扩展机制包括约束、构造型和标记值&#xff0c;其中的构造型定义…

Github、Gitee优秀的开源项目分享

先赞后看&#xff0c;养成习惯&#xff01;&#xff01;&#xff01;❤️ ❤️ ❤️ 资源收集不易&#xff0c;如果喜欢可以关注我哦&#xff01; ​如果本篇内容对你有所启发&#xff0c;欢迎访问我的个人博客了解更多内容&#xff1a;链接地址 ​ Java 项目 javacore - Java …

电子印章法律风险点及安全防范措施

公章是公司处理内外部事务的印鉴&#xff0c;公司对外的正式信函、文件、报告使用公章&#xff0c;盖了公章的文件具有法律效力。公章由公司的法定代表人执掌&#xff0c;法定代表人如果把法定代表人章与公章一同使用就代表公司行为。 随着社会数字化转型&#xff0c;电子印章及…

SLAM算法与工程实践——相机篇:传统相机使用(1)

SLAM算法与工程实践系列文章 下面是SLAM算法与工程实践系列文章的总链接&#xff0c;本人发表这个系列的文章链接均收录于此 SLAM算法与工程实践系列文章链接 下面是专栏地址&#xff1a; SLAM算法与工程实践系列专栏 文章目录 SLAM算法与工程实践系列文章SLAM算法与工程实践…

登录/验证码/注册

登录 pom文件 <!--hutool工具类--><dependency><groupId>cn.hutool</groupId><artifactId>hutool-all</artifactId><version>5.8.9</version></dependency><!--jwt--><dependency><groupId>io.jsonw…

maui 调用文心一言开发的聊天APP 3

主要是对代码进行了优化 上一个版本写死了帐号跟密码 &#xff0c;这一个帐本有户可以直接设置对相关的key以及secret如果设置错时&#xff0c;在聊天中也会返回提示。注册帐号时同时也设置了key及secrete升级到了net.8.0导出APK&#xff0c;上一个版本是导出abb.解决了变型问…

Redis新数据类型-Bitmaps

目录 Bitmaps 简介 命令 1. setbit (1) 格式 (2) 实例 2. getbit (1) 格式 (2) 实例 3. bitcount (1) 格式 (2) 实例 4. bitop (1) 格式 (2) 实例 我的其他博客 Bitmaps 简介 Bitmaps 是 Redis 的一种新数据类型&#xff0c;它是一种用于存储位信息的数据结构&…

Netty详细文档

Netty教程 文章目录 Netty教程 Netty简介Netty 的介绍Netty 的应用场景互联网行业游戏行业大数据领域其它开源项目使用到 Netty Netty 的学习资料参考 Java BIO编程I/O 模型BIO、NIO、AIO 使用场景分析Java BIO 基本介绍Java BIO 工作机制Java BIO 应用实例问题分析 Java NIO编…

腾讯云Elasticsearch Service产品体验

基本介绍 产品概述 腾讯云 Elasticsearch Service&#xff08;ES&#xff09;是云端全托管海量数据检索分析服务&#xff0c;拥有高性能自研内核&#xff0c;集成X-Pack。ES 支持通过自治索引、存算分离、集群巡检等特性轻松管理集群&#xff0c;也支持免运维、自动弹性、按需…

iOS使用CoreText完成txt阅读器

CoreText是一个高效处理字符和字形转换和进行文字排版的框架&#xff0c;API基于C语言。 常见的CoreText类介绍 &#xff08;1&#xff09;、CFAttributedStringRef 属性字符串&#xff0c;用于存储需要绘制的文字字符和字符属性 &#xff08;2&#xff09;、CTFramesetterR…

Layui实现自定义的table列悬停事件并气泡提示信息

1、概要 使用layui组件实现table的指定列悬停时提示信息&#xff0c;因为layui组件中没有鼠标悬停事件支持&#xff0c;所以需要结合js原生事件来实现这个功能&#xff0c;并结合layui的tips和列的templte属性气泡提示实现效果。 2、效果图 3、代码案例 <!DOCTYPE html&g…

Spark编程入门

1.8 Spark编程入门 1.8.1 通过IDEA创建Spark工程 ps:工程创建之前步骤省略,在scala中已经讲解,直接默认是创建好工程的 导入Pom文件依赖 <!-- 声明公有的属性 --><properties><maven.compiler.source>1.8</maven.compiler.source><maven.compiler…

企业电子招投标采购系统源码之鸿鹄电子招投标系统+电子招投标的组成

招投标管理系统是一款适用于招标代理、政府采购、企业采购和工程交易等领域的企业级应用平台。该平台以项目为主线&#xff0c;从项目立项到项目归档&#xff0c;实现了全流程的高效沟通和协作。通过该平台&#xff0c;用户可以实时共享项目数据信息&#xff0c;实现规范化管理…

基于VUE3+Layui从头搭建通用后台管理系统(前端篇)十四:系统设置模块相关功能实现

一、本章内容 本章使用已实现的公共组件实现系统管理中的系统设置模块相关功能,包括菜单管理、角色管理、日志管理、用户管理、系统配置、数据字典等。 1. 详细课程地址: 待发布 2. 源码下载地址: 待发布 二、界面预览 三、开发视频 3.1 B站视频地址:

Duplicate keys detected: This may cause an update error.【Vue遍历渲染报错的解决】

今天在写项目时&#xff0c;写到一个嵌套评论的遍历时&#xff0c;控制台出现了一个报错信息&#xff0c;但是并不影响页面的渲染&#xff0c;然后一看这个错的原因是 key值重复&#xff0c;那么问题的解决方式就很简单了。&#xff08;vue for循环读取key值时&#xff0c; key…