进程与线程的同步、互斥

————————————————————————

进程的同步、互斥

通过 信号灯集(semaphore)实现。

信号灯集 —— 实现同步

待完善。

信号灯集 —— 实现互斥

待完善。

💡练习:打印 AB

💡同步实现
// prog_syn.c

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>

#define KEY 20230804

void print_char(char ch) {
    printf("%c ", ch);
    fflush(stdout);
}

// P操作,申请资源
void semaphore_p(int sem_id, int sem_num) {
    struct sembuf sem_buf = {sem_num, -1, 0};
    semop(sem_id, &sem_buf, 1);
}

// V操作,释放资源
void semaphore_v(int sem_id, int sem_num) {
    struct sembuf sem_buf = {sem_num, 1, 0};
    semop(sem_id, &sem_buf, 1);
}

int main(int argc, char const *argv[]) {
    // 创建信号量集
    int sem_id = semget(KEY, 2, IPC_CREAT | 0666);
    if (sem_id == -1) {
        perror("Failed to semget");
        return -1;
    }

    // 初始化信号量集
    union semun {
        int val;
        struct semid_ds *buf;
        ushort *array;
    } arg;

    arg.array = (ushort *)malloc(sizeof(ushort) * 2);
    arg.array[0] = 1;  // 初始化信号量0为1,表示A进程可以打印
    arg.array[1] = 0;  // 初始化信号量1为0,表示B进程等待

    if (semctl(sem_id, 0, SETALL, arg) == -1) {
        perror("Failed to semctl");
        return -1;
    }

    pid_t pid = fork();
    if (pid < 0) {
        perror("Failed to fork");
        return -1;
    } else if (pid == 0) {
        // 子进程,负责打印字符B
        while (1) {
            semaphore_p(sem_id, 1);  // 等待信号量1
            print_char('B');
            semaphore_v(sem_id, 0);  // 发送信号量0
        }
    } else {
        // 父进程,负责打印字符A
        while (1) {
            sleep(1);
            semaphore_p(sem_id, 0);  // 等待信号量0
            print_char('A');
            semaphore_v(sem_id, 1);  // 发送信号量1
        }
    }

    // 销毁信号量集
    if (semctl(sem_id, 0, IPC_RMID) == -1) {
        perror("Failed to semctl");
        return -1;
    }

    return 0;
}

运行结果如下:
在这里插入图片描述

💡互斥实现
// prog_mut.c

#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/sem.h>
#include <sys/ipc.h>
#include <stdlib.h>
#include <sys/wait.h>

// 定义信号灯集的键值
#define SEM_KEY 8041942

// 定义信号量操作结构体
struct sembuf sem_operation;

void print_char(char ch) {
    printf("%c ", ch);
    fflush(stdout);
}

// P操作,申请资源
void semaphore_p(int sem_id, int sem_num) {
    struct sembuf sem_buf = {sem_num, -1, 0};
    semop(sem_id, &sem_buf, 1);
}

// V操作,释放资源
void semaphore_v(int sem_id, int sem_num) {
    struct sembuf sem_buf = {sem_num, 1, 0};
    semop(sem_id, &sem_buf, 1);
}

int main(int argc, char const *argv[]) {

    // 创建信号灯集
    int sem_id = semget(SEM_KEY, 1, IPC_CREAT | 0666);
    if (sem_id == -1) {
        perror("Failed to create semaphore set");
        return 1;
    }

    // 初始化信号量集
    union semun {
        int val;
        struct semid_ds *buf;
        ushort *array;
    } arg;

    arg.array = (ushort *)malloc(sizeof(ushort) * 1);
    arg.array[0] = 1;  // 初始化信号量0为1,A\B进程抢夺资源

    // 初始化信号量为1,表示可用
    if (semctl(sem_id, 0, SETVAL, 1) == -1) {
        perror("Failed to initialize semaphore");
        return 1;
    }

    // 创建子进程
    pid_t pid = fork();
    if (pid == -1) {
        perror("Failed to fork");
        return 1;
    }

    if (pid == 0) {
        // 子进程
        for (int i = 0; i < 10; i++) {
            // sleep(1);               // 情况二:此句 sleep 被注释掉,下句 sleep 取消注释
            // 进入临界区前等待信号量可用
            semaphore_p(sem_id, 0);

            // 进入临界区,打印字母B
            print_char('B');

            // 释放信号量,表示临界区可用
            semaphore_v(sem_id, 0);
        }
    } else {
        // 父进程
        for (int i = 0; i < 10; i++) {
            // sleep(1);				// 情况一:此句 sleep 被注释掉,上句 sleep 取消注释
            // 进入临界区前等待信号量可用
            semaphore_p(sem_id, 0);

            // 进入临界区,打印字母A
            print_char('A');

            // 释放信号量,表示临界区可用
            semaphore_v(sem_id, 0);
        }
        wait(NULL);
    }

    putchar(10);

    // 删除信号灯集
    // if (semctl(sem_id, 0, IPC_RMID) == -1) {
    //     perror("Failed to remove semaphore set");
    //     return -1;
    // }
    
    return 0;
}

运行结果如下:
在这里插入图片描述

————————————————————————

线程的同步、互斥

通过 信号量 可以实现线程的同步和互斥;
通过 互斥锁 可以实现线程的互斥;
通过 互斥锁 + 条件变量 可以实现线程的同步。

信号量 —— 实现同步

sem_t sem1, sem2, …, sem_n;
sem_init(&sem1, 0, 1);
sem_init(&sem2, 0, 0);

sem_init(&sem_n, 0, 0);

*thread_1{
sem_wait(&sem1);
// 临界区
sem_post(&sem2);
}
*thread_2{
sem_wait(&sem2);
// 临界区
sem_post(&sem3);
}

*thread_n{
sem_wait(&sem_n);
// 临界区
sem_post(&sem_1);
}

信号量 —— 实现互斥

sem_t sem;
sem_init(&sem, 0, 1);
*thread_1{
sem_wait(&sem);
// 临界区
sem_post(&sem);
}
*thread_2{
sem_wait(&sem);
// 临界区
sem_post(&sem);
}

*thread_n{
sem_wait(&sem);
// 临界区
sem_post(&sem);
}

💡练习:字符串正序、倒序输出

💡同步实现
// sem_syn.c

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

char buf[64] = "";
sem_t sem1, sem2;

void *reverse(void *arg){
    while (1){
        char *p = buf;
        char *head = p;
        char *tail = NULL;
        while (*(p+1) != '\0')
            p++;
        tail = p;

        sem_wait(&sem1);
        while (head < tail){
            *head ^= *tail;
            *tail ^= *head;
            *head++ ^= *tail--;
        }
        sem_post(&sem2);
    }
    pthread_exit(0);
}

void *output(void *arg){

    while (1){
        sleep(1);							// 只加在一个线程函数内即可
        sem_wait(&sem2);
        printf("%s\n", buf);
        sem_post(&sem1);
    }
    
    pthread_exit(0);
}

int main(int argc, char const *argv[])
{
    sem_init(&sem1, 0, 1);
    sem_init(&sem2, 0, 0);

    strcpy(buf, "Welcome to my world!");

    pthread_t tid1, tid2;
    if (pthread_create(&tid1, NULL, output, NULL) != 0){
        perror("Failed to create a thread");
        return -1;
    }
    if (pthread_create(&tid2, NULL, reverse, NULL) != 0){
        perror("Failed to create a thread");
        return -1;
    }

    pthread_join(tid1, NULL);
    pthread_join(tid2, NULL);
    return 0;
}

运行结果如下:
在这里插入图片描述

💡互斥实现(无规律)
// sem_mut.c

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

char buf[64] = "";
sem_t sem;

void *reverse(void *arg){
    while (1){
        char *p = buf;
        char *head = p;
        char *tail = NULL;
        while (*(p+1) != '\0')
            p++;
        tail = p;

        sem_wait(&sem);
        while (head < tail){
            *head ^= *tail;
            *tail ^= *head;
            *head++ ^= *tail--;
        }
        sem_post(&sem);
    }
    pthread_exit(0);
}

void *output(void *arg){

    while (1){
        sleep(1);									// 只加在一个线程函数内即可
        sem_wait(&sem);
        printf("%s\n", buf);
        sem_post(&sem);
    }
    
    pthread_exit(0);
}

int main(int argc, char const *argv[])
{
    sem_init(&sem, 0, 1);

    strcpy(buf, "Welcome to my world!");

    pthread_t tid1, tid2;
    if (pthread_create(&tid1, NULL, output, NULL) != 0){
        perror("Failed to create a thread");
        return -1;
    }
    if (pthread_create(&tid2, NULL, reverse, NULL) != 0){
        perror("Failed to create a thread");
        return -1;
    }

    pthread_join(tid1, NULL);
    pthread_join(tid2, NULL);
    return 0;
}

运行结果如下:
在这里插入图片描述

— — — — — — — — — — — — — — — — — — — — — — — — — — —

互斥锁 —— 实现互斥

pthread_mutex_t lock;

*thread_1{
pthread_mutex_lock(&lock);
// 临界区
pthread_mutex_unlock(&lock);
}
*thread_2{
pthread_mutex_lock(&lock);
// 临界区
pthread_mutex_unlock(&lock);
}

*thread_n{
pthread_mutex_lock(&lock);
// 临界区
pthread_mutex_unlock(&lock);
}

互斥锁 + 条件变量 —— 实现同步

双线程:可以先让其中一个线程 sleep,以确保另一个线程先抢到时间片。
多线程:定义 flag = 0; 在每个线程中修改 flag 的值。pthread_cond_broadcast(&condition);

pthread_mutex_t lock;
pthread_cond_t condition;
int flag = 0;

*thread_1{
pthread_mutex_lock(&lock);
while(flag != 0)
pthread_cond_wait(&condition, &lock);
// 临界区
flag = 1;
pthread_cond_broadcast(&condition);
pthread_mutex_unlock(&lock);
}
*thread_2{
pthread_mutex_lock(&lock);
while(flag != 1)
pthread_cond_wait(&condition, &lock);
// 临界区
flag = 2;
pthread_cond_broadcast(&condition);
pthread_mutex_unlock(&lock);
}

*thread_n{
pthread_mutex_lock(&lock);
while(flag != n-1)
pthread_cond_wait(&condition, &lock);
// 临界区
flag = n;
pthread_cond_broadcast(&condition);
pthread_mutex_unlock(&lock);
}

💡练习:打印 ABC

💡互斥实现(乱序):
// mutex_mut.c

#include <stdio.h>
#include <pthread.h>
#include <semaphore.h>
#include <unistd.h>

pthread_mutex_t lock;

void *print1(void *arg){

    for (int i = 1; i <= 6; i++){

        pthread_mutex_lock(&lock);
        printf("%d: A\t", i);
        pthread_mutex_unlock(&lock);
    }
    pthread_exit(0);
}

void *print2(void *arg){

    for (int i = 1; i <= 6; i++){
        sleep(1);
        pthread_mutex_lock(&lock);
        printf("%d: B\t", i);
        pthread_mutex_unlock(&lock);
    }
    pthread_exit(0);
}

void *print3(void *arg){

    for (int i = 1; i <= 6; i++){
        sleep(2);
        pthread_mutex_lock(&lock);
        printf("%d: C\t", i);
        putchar(10); 
        pthread_mutex_unlock(&lock);
    }
    pthread_exit(0);
}

int main(int argc, char const *argv[])
{
    pthread_t tid1, tid2, tid3;
    if (pthread_create(&tid1, NULL, print1, NULL)){
        perror("Failed: ");
        return -1;
    }
    if (pthread_create(&tid2, NULL, print2, NULL)){
        perror("Failed: ");
        return -1;
    }
    if (pthread_create(&tid3, NULL, print3, NULL)){
        perror("Failed: ");
        return -1;
    }

    pthread_join(tid1, NULL);
    pthread_join(tid2, NULL);
    pthread_join(tid3, NULL);
    pthread_mutex_destroy(&lock);

    return 0;
}

运行结果如下:
在这里插入图片描述

💡同步实现:
// mutex_syn.c

#include <stdio.h>
#include <pthread.h>
#include <semaphore.h>
#include <unistd.h>

pthread_mutex_t lock;
pthread_cond_t condition;
int flag = 0;

void *print1(void *arg){

    for (int i = 1; ; i++){

        pthread_mutex_lock(&lock);
        while(flag != 0)
            pthread_cond_wait(&condition, &lock);

        printf("%d: A\t", i);

        flag = 1;
        pthread_cond_broadcast(&condition);
        pthread_mutex_unlock(&lock);
    }
    pthread_exit(0);
}

void *print2(void *arg){

    for (int i = 1; ; i++){

        pthread_mutex_lock(&lock);
        while(flag != 1)
            pthread_cond_wait(&condition, &lock);

        printf("%d: B\t", i);

        flag = 2;
        pthread_cond_broadcast(&condition);
        pthread_mutex_unlock(&lock);
    }
    pthread_exit(0);
}

void *print3(void *arg){

    for (int i = 1; ; i++){
        sleep(1);									// 只加在一个线程函数内即可
        
        pthread_mutex_lock(&lock);
        while(flag != 2)
            pthread_cond_wait(&condition, &lock);

        printf("%d: C\t", i);
        putchar(10); 

        flag = 0;
        pthread_cond_broadcast(&condition);
        pthread_mutex_unlock(&lock);
    }
    pthread_exit(0);
}

int main(int argc, char const *argv[])
{
    pthread_t tid1, tid2, tid3;
    if (pthread_create(&tid1, NULL, print1, NULL)){
        perror("Failed: ");
        return -1;
    }
    if (pthread_create(&tid2, NULL, print2, NULL)){
        perror("Failed: ");
        return -1;
    }
    if (pthread_create(&tid3, NULL, print3, NULL)){
        perror("Failed: ");
        return -1;
    }

    pthread_join(tid1, NULL);
    pthread_join(tid2, NULL);
    pthread_join(tid3, NULL);
    pthread_mutex_destroy(&lock);
    pthread_cond_destroy(&condition);

    return 0;
}

运行结果如下:
在这里插入图片描述

————————————————————————

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值