Study notes on thread synchronization, mutex deadlock, producer and consumer models, and semaphore related knowledge

1. Thread synchronization

Synchronization is the coordinated pace, running in a predetermined order. Such as: You finish, I will say. Literally, the word "same" is easy to understand as acting together, but it is not. The word "same" should refer to coordination, assistance, and mutual cooperation.

For example, process and thread synchronization can be understood as the cooperation between process or thread A and B. When A executes to a certain extent, it depends on a certain result of B, so it stops and signals B to run; B executes as it says, and then gives the result to A , A continues to operate.

In multi-threaded programming, some sensitive data are not allowed to be accessed by multiple threads at the same time. At this time, the synchronous access technology is used to ensure that the data is accessed by at most one thread at any time to ensure the integrity of the data.

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

int num = 0;
void* run(void* arg)
{
    
    
	for(int i=0; i<1000000; i++)
	{
    
    
		num++;
	}
}

int main(int argc,const char* argv[])
{
    
    
	pthread_t tid1,tid2;
	pthread_create(&tid1,NULL,run,NULL);
	pthread_create(&tid2,NULL,run,NULL);
	pthread_join(tid1,NULL);
	pthread_join(tid2,NULL);
	printf("%d\n",num);
}

2. Mutex lock

注意:如果man手册中查不到这系列函数,可以安装以下内容:
	sudo apt-get install glibc-doc
	sudo apt-get install manpages-posix-dev
    
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
功能:定义并初始化互斥锁
    
int pthread_mutex_init (pthread_mutex_t* mutex,const pthread_mutexattr_t*
mutexattr);
功能:初始化一互斥锁,会被初始化为非锁定状态
    
int pthread_mutex_lock (pthread_mutex_t* mutex);
功能:加锁,当互斥锁已经是锁定状态时,调用者会阻塞,直到互斥被解开,当前线程才会加锁成功并返回。
    
int pthread_mutex_unlock (pthread_mutex_t* mutex);
功能:解锁,解锁后等待加锁的线程才能加锁成功。
    
int pthread_mutex_destroy (pthread_mutex_t* mutex);
功能:销毁锁
    
int pthread_mutex_trylock (pthread_mutex_t *__mutex)
功能:加测试锁,如果不加锁刚立即返回
    
int pthread_mutex_timedlock(pthread_mutex_t *restrict mutex,
							const struct timespec *restrict abs_timeout);
功能:倒计时加锁,如果超时还不加上则立即返回。
struct timespec{
    
    
	time_t tv_sec; 		/* Seconds. */
	long int tv_nsec; 	/* Nanoseconds.*/ 1= 1000000000 纳秒
};
#include <stdio.h>
#include <pthread.h>
/*
执行流程:
	1、互斥锁被初始化为非锁定状态
	2、线程1调用pthread_mutex_lock函数,立即返回,互斥量呈锁定状态;
	3、线程2调用pthread_mutex_lock函数,阻塞等待;
	4、线程1调用pthread_mutex_unlock函数,互斥量呈非锁定状态;
	5、线程2被唤醒,从pthread_mutex_lock函数中返回,互斥量呈锁定状态
*/

pthread_mutex_t mutex;
//pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;

int num = 0;
void* run(void* arg)
{
    
    
	for(int i=0; i<1000000; i++)
	{
    
    
		pthread_mutex_lock(&mutex);
		num++;
		pthread_mutex_unlock(&mutex);
	}
}

int main(int argc,const char* argv[])
{
    
    
    pthread_mutex_init(&mutex,NULL);
    pthread_t pid1,pid2;
    pthread_create(&pid1,NULL,run,NULL);
    pthread_create(&pid2,NULL,run,NULL);
    pthread_join(pid1,NULL);
    pthread_join(pid2,NULL);
    pthread_mutex_destroy(&mutex);
    printf("%d\n",num);
}

3. Read-write lock

pthread_rwlock_t rwlock = PTHREAD_RWLOCK_INITIALIZER;
功能:定义并初始化读写锁
    
int pthread_rwlock_init(pthread_rwlock_t *restrict rwlock,
						const pthread_rwlockattr_t *restrict attr);
功能:初始化读写锁
    
int pthread_rwlock_rdlock(pthread_rwlock_t *rwlock);
功能:加读锁,如果不能加则阻塞等待
    
int pthread_rwlock_wrlock(pthread_rwlock_t *rwlock);
功能:加写锁,如果不能加则阻塞等待
    
int pthread_rwlock_tryrdlock(pthread_rwlock_t *rwlock);
功能:尝试加读锁,如果不能加则立即返回
    
int pthread_rwlock_trywrlock(pthread_rwlock_t *rwlock);
功能:尝试加写锁,如果不能加则立即返回
    
int pthread_rwlock_timedrdlock(pthread_rwlock_t *restrict rwlock,
								const struct timespec *restrict abstime);
功能:带倒计时加读锁,超时则立即返回
    
int pthread_rwlock_timedwrlock(pthread_rwlock_t *restrict rwlock,
								const struct timespec *restrict abstime);
功能:带倒计时加写锁,超时则立即返回
    
int pthread_rwlock_destroy(pthread_rwlock_t *rwlock);
功能:销毁读写锁
    
使用读写锁的线程应根据后续的操作进行加锁,如果只对数据进行读取则只加读锁即可,只有对数据进行修改时才应该加写锁,与互斥锁的区别是,它能让只读的线程加上锁,使用原理与文件锁一样。
    线程A 线程B
    读锁 读锁 OK
    读锁 写锁 NO
    写锁 读锁 NO
    写锁 写锁 NO
#include <pthread.h>

// 创建读写锁
pthread_rwlock_t rwlock;

int num = 0;
void* run(void* arg)
{
    
    
	for(int i=0; i<1000000; i++)
	{
    
    
		// 加写锁
		pthread_rwlock_wrlock(&rwlock);
		num++;
		// 解锁
		pthread_rwlock_unlock(&rwlock);
	}
}

int main(int argc,const char* argv[])
{
    
    
	// 初始化读写锁
    pthread_rwlock_init(&rwlock,NULL);
    pthread_t pid1,pid2;
    pthread_create(&pid1,NULL,run,NULL);
    pthread_create(&pid2,NULL,run,NULL);
    pthread_join(pid1,NULL);
    pthread_join(pid2,NULL);
    printf("%d\n",num);
}

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

// 创建读写锁
pthread_rwlock_t rwlock;

void* run1(void* arg)
{
    
    
    // 加读锁
    pthread_rwlock_rdlock(&rwlock);
    sleep(1);
    printf("%lu 线程1加读锁成功!\n",pthread_self());
    // 解锁
    pthread_rwlock_unlock(&rwlock);
}
void* run2(void* arg)
{
    
    
    // 加读锁
    pthread_rwlock_rdlock(&rwlock);
    sleep(1);
    printf("%lu 线程2加读锁成功!\n",pthread_self());
    // 解锁
    pthread_rwlock_unlock(&rwlock);
}

int main(int argc,const char* argv[])
{
    
    
    // 初始化读写锁
    pthread_rwlock_init(&rwlock,NULL);
    pthread_t pid1,pid2;
    pthread_create(&pid1,NULL,run1,NULL);
    pthread_create(&pid2,NULL,run2,NULL);
    pthread_join(pid1,NULL);
    pthread_join(pid2,NULL);
    // 销毁读写锁
    pthread_rwlock_destroy(&rwlock);
}

4. Deadlock problem

What is a deadlock:

​ Multiple threads wait for each other's resources, and will not release their own resources until they get the resources they need, and then cause a phenomenon of circular waiting, which is called deadlock.

There are four necessary conditions for a deadlock to occur:

1. Resource mutual exclusion

2. Possess and wait

3. Resources are inalienable

4. Loop waiting

The above four conditions are indispensable, as long as one of them is not satisfied, it cannot constitute a deadlock.

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

// 创建三个互斥锁并初始化
pthread_mutex_t mutex1 = PTHREAD_MUTEX_INITIALIZER;
pthread_mutex_t mutex2 = PTHREAD_MUTEX_INITIALIZER;
pthread_mutex_t mutex3 = PTHREAD_MUTEX_INITIALIZER;

void* run1(void* arg)
{
    
    
    pthread_mutex_lock(&mutex1);
    usleep(100);
    pthread_mutex_lock(&mutex2);
    printf("没有构成死锁!!!\n");
    pthread_mutex_unlock(&mutex2);
    pthread_mutex_unlock(&mutex1);
}

void* run2(void* arg)
{
    
    
    pthread_mutex_lock(&mutex2);
    usleep(100);
    pthread_mutex_lock(&mutex3);
    printf("没有构成死锁!!!\n");
    pthread_mutex_unlock(&mutex3);
    pthread_mutex_unlock(&mutex2);
}

void* run3(void* arg)
{
    
    
    pthread_mutex_lock(&mutex3);
    usleep(100);
    pthread_mutex_lock(&mutex1);
    printf("没有构成死锁!!!\n");
    pthread_mutex_unlock(&mutex1);
    pthread_mutex_unlock(&mutex3);
}

int main(int argc,const char* argv[])
{
    
    
    // 创建三个线程
    pthread_t tid1,tid2,tid3;
    pthread_create(&tid1,NULL,run1,NULL);
    pthread_create(&tid2,NULL,run2,NULL);
    pthread_create(&tid3,NULL,run3,NULL);
    // 主线程等待三个子线程结束
    pthread_join(tid1,NULL);
    pthread_join(tid2,NULL);
    pthread_join(tid3,NULL);
    return 0;
}

How to prevent deadlocks:

If only one of the four conditions that constitute a deadlock is not true, a deadlock will not occur.

1. Destroy the mutually exclusive conditions so that resources can be shared (prepare multiple copies).

2. Destroy the conditions of possession and waiting, apply for all the resources it needs at one time, do not let it run until the resources are not satisfied, and once it starts running, it will always be owned by it. The disadvantage is that system resources will be wasted.

3. Break the inalienable condition. When you already occupy some resources and request new resources but cannot get them, you will release the resources you have already obtained. The disadvantage is that it is more complicated to implement, and releasing the resources you have already obtained may cause previous problems. One stage of work wasted.

4. Destroy the condition of circular waiting, adopt the method of sequential allocation of resources, number resources in the system, stipulate that threads must obtain resources in the order of increasing number, the disadvantage is that resources must be relatively stable, which limits the increase and decrease of resources .

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

// 创建三个互斥锁并初始化
pthread_mutex_t mutex1 = PTHREAD_MUTEX_INITIALIZER;
pthread_mutex_t mutex2 = PTHREAD_MUTEX_INITIALIZER;
pthread_mutex_t mutex3 = PTHREAD_MUTEX_INITIALIZER;

void* run1(void* arg)
{
    
    
	while(1)
	{
    
    
        pthread_mutex_lock(&mutex1);
        usleep(100);
        if(0 == pthread_mutex_trylock(&mutex2))
        break;
        pthread_mutex_unlock(&mutex1);
    }
    printf("没有构成死锁!!!\n");
    pthread_mutex_unlock(&mutex2);
    pthread_mutex_unlock(&mutex1);
}

void* run2(void* arg)	
{
    
    
	while(1)
	{
    
    
        pthread_mutex_lock(&mutex2);
        usleep(100);
        if(0 == pthread_mutex_trylock(&mutex3))
        break;
        pthread_mutex_unlock(&mutex2);
    }
    printf("没有构成死锁!!!\n");
    pthread_mutex_unlock(&mutex3);
    pthread_mutex_unlock(&mutex2);
}

void* run3(void* arg)
{
    
    
	while(1)
	{
    
    
        pthread_mutex_lock(&mutex3);
        usleep(100);
        if(0 == pthread_mutex_trylock(&mutex1))
        break;
        pthread_mutex_unlock(&mutex3);
	}
    printf("没有构成死锁!!!\n");
    pthread_mutex_unlock(&mutex1);
    pthread_mutex_unlock(&mutex3);
}

int main(int argc,const char* argv[])
{
    
    
    // 创建三个线程
    pthread_t tid1,tid2,tid3;
    pthread_create(&tid1,NULL,run1,NULL);
    pthread_create(&tid2,NULL,run2,NULL);
    pthread_create(&tid3,NULL,run3,NULL);
    // 主线程等待三个子线程结束
    pthread_join(tid1,NULL);
    pthread_join(tid2,NULL);
    pthread_join(tid3,NULL);
    return 0;
}

Ways to detect deadlock:

General idea: observation + analysis

Method 1: Read the code and analyze the locking steps of each thread.

Method 2: Use strace to trace the execution flow of the program.

Method 3: View the business execution process of the log watcher.

Method 4: Use gdb to debug and check the execution status of each thread.

1、把断点打在线程创建完毕后
2、run
3、info threads 查看所有线程
4、thread n 进程指定的线程
5、bt 查看线程堆栈信息
6、配合s/n单步调试

5. Atomic operations

The so-called atomic operation is an operation that cannot be split. For multi-threaded operations on global variables, there is no need for thread locks anymore. It has the same protective effect as pthread_mutex_t, and it is also thread-safe. Some compilers use it when using Need to add -march=i686 compilation parameters.

type __sync_fetch_and_add (type *ptr, type value); // +
type __sync_fetch_and_sub (type *ptr, type value); // -
type __sync_fetch_and_and (type *ptr, type value); // &
type __sync_fetch_and_or (type *ptr, type value); // |
type __sync_fetch_and_nand (type *ptr, type value); // ~
type __sync_fetch_and_xor (type *ptr, type value); // ^
功能:以上操作返回的是*ptr的旧值
    
type __sync_add_and_fetch (type *ptr, type value); // +
type __sync_sub_and_fetch (type *ptr, type value); // -
type __sync_and_and_fetch (type *ptr, type value); // &
type __sync_or_and_fetch (type *ptr, type value); // |
type __sync_nand_and_fetch (type *ptr, type value); // ~
type __sync_xor_and_fetch (type *ptr, type value); // ^
功能:以上操作返回的是*ptr与value计算后的值
    
type __sync_lock_test_and_set (type *ptr, type value);
功能:把value赋值给*ptr,并返回*ptr的旧值
    
__sync_lock_release(type *ptr);
功能:将*ptr赋值为0
#include <stdio.h>
#include <pthread.h>

int num = 0;
void* run(void* arg)
{
    
    
    for(int i=0; i<100000000; i++)
    {
    
    
        __sync_fetch_and_add(&num,1);
    }
}
int main(int argc,const char* argv[])
{
    
    
    pthread_t pid1,pid2;
    pthread_create(&pid1,NULL,run,NULL);
    pthread_create(&pid2,NULL,run,NULL);
    pthread_join(pid1,NULL);
    pthread_join(pid2,NULL);
    printf("%d\n",num);
}

Advantages of atomic operations:

1. The speed is extremely fast

2. There will be no deadlock

Disadvantages of atomic operations:

1. This function is not universal, and some compilers do not support it.

2. type can only be an integer-related type, and floating-point and custom types cannot be used.

6. Producer and consumer model

Producer: The thread that produces data. This type of thread is responsible for receiving data from the client and the client, and then pushing the data to the storage intermediary.

Consumer: The thread responsible for consuming data, and processing (judging, filtering, using, responding, storing) the data produced by the producer thread.
Storage intermediary: also called data warehouse, it is a data buffer between producer thread and consumer thread, which is used to balance the unbalanced production speed and consumption speed between the two, and isolates producers and consumers through the buffer , compared with the direct connection between the two, it avoids waiting for each other and improves operating efficiency.

Problem 1: Production is faster than consumption, the buffer is full, and it is exhausted.

Solution: The thread in charge of production notifies the thread in charge of consumption to consume at full speed, and then go to sleep.

Problem 2: Consuming faster than producing, buffer empty, starvation.

Solution: The thread in charge of consumption informs the thread in charge of production to produce at full speed, and then goes to sleep.

7. Condition variables

Condition variables are a mechanism for synchronization using "global variables" shared between threads, mainly including two actions:

​ 1. The thread waits for "the condition of the condition variable to be established" and sleeps;

2. Wait for the "condition to be established" to wake up the sleeping thread.

To prevent race conditions, the use of condition variables is always combined with a mutex.

int pthread_cond_init (pthread_cond_t* cond,const pthread_condattr_t* attr);
//亦可pthread_cond_t cond = PTHREAD_COND_INITIALIZER;

// 使调用线程睡入条件变量cond,同时释放互斥锁mutex
int pthread_cond_wait (pthread_cond_t* cond,pthread_mutex_t* mutex);

int pthread_cond_timedwait (pthread_cond_t* cond,
    pthread_mutex_t* mutex,
    const struct timespec* abstime);

struct timespec {
    
    
    time_t tv_sec; // Seconds
    long tv_nsec; // Nanoseconds [0 - 999999999]
};

// 从条件变量cond中唤出一个线程,
// 令其重新获得原先的互斥锁
int pthread_cond_signal (pthread_cond_t* cond);

注意:被唤出的线程此刻将从pthread_cond_wait函数中返回,
但如果该线程无法获得原先的锁,则会继续阻塞在加锁上。
    
// 从条件变量cond中唤醒所有线程
int pthread_cond_broadcast (pthread_cond_t* cond);

// 销毁条件变量
int pthread_cond_destroy (pthread_cond_t* cond);
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>
// 仓库最大容量
#define STACK_MAX 50

// 仓库
char stack[STACK_MAX];
// 仓库的入口和出口
int top;

// 保护仓库入口的互斥锁
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
// 当仓库满时线程睡入的条件变量
pthread_cond_t full = PTHREAD_COND_INITIALIZER;
// 当仓库空时线程睡入的条件变量
pthread_cond_t empty = PTHREAD_COND_INITIALIZER;

// 显示仓库内容
void show_stack(const char* opt,char data)
{
    
    
    if(50 < top || 0 > top)
    {
    
    
        printf("爆仓了 %d\n",top);
        exit(0);
    }
    for(int i=0; i<top; i++)
    {
    
    
        printf("%c",stack[i]);
    }
    printf("%s%c\n",opt,data);
}
// 生产者
void* production(void* arg)
{
    
    
    for(;;)
    {
    
    
        // 往仓库中push数据,加锁保护入口
        pthread_mutex_lock(&mutex);
        // 发现仓库满了
        while(top >= STACK_MAX)
        {
    
    
            // 叫睡所有消费线程全速消费,虽然暂时只能醒来一个消费者线程,但所有消费线程已经加
            入到争夺互斥锁的过程
            pthread_cond_broadcast(&empty);
            // 生产者线程睡入满仓条件变量
            pthread_cond_wait(&full,&mutex);
        }
        
        // 生产数据
        char data = rand()%26+'A';
        // 显示仓库
        show_stack("<-",data);
        // 数据添加到仓库
        stack[top++] = data;
        // 模拟生产数据消耗的时间
        usleep(rand()%5*10000);
        
        // 此时仓库已经不空了,叫醒一个空仓条件休眠的消费者线程
        pthread_cond_signal(&empty);
        
        // 生产完毕解锁入口
        pthread_mutex_unlock(&mutex);
    }
}

// 消费者
void* consumption(void* arg)
{
    
    
    for(;;)
    {
    
    
        // 从仓库中消费数据,加锁保护出口
        pthread_mutex_lock(&mutex);
        // 发现空仓
        while(0 == top)
        {
    
    
            // 叫醒所有生产线程全速生产,虽然暂时只能醒来一个生产者线程,但所有生产者线程都加
            入到争夺互斥锁的过程
            pthread_cond_broadcast(&full);
            // 消费者线程睡入空仓条件变量
            pthread_cond_wait(&empty,&mutex);
        }
        
        // 从仓库中消费数据
        char data = stack[top--];
        // 显示仓库内容
        show_stack("->",data);
        // 模拟消费数据所消耗的时间
        usleep(rand()%5*10000);
        
        // 此时已经不满,叫醒一个因为满仓而休眠的线程
        pthread_cond_signal(&full);
        
        // 消费完毕解锁出口
        pthread_mutex_unlock(&mutex);
    }
}

int main(int argc,const char* argv[])
{
    
    
    srand(time(NULL));
    
    pthread_t tids[10];
    for(int i=0; i<10; i++)
    {
    
    
        if(i%2)
        	pthread_create(tids+i,NULL,production,NULL);
        else
        	pthread_create(tids+i,NULL,consumption,NULL);
    }
    for(int i=0; i<10; i++)
    {
    
    
        pthread_join(tids[i],NULL);
    }
    return 0;
}

Eight, semaphore

Semaphores used by multiple threads:

#include <semaphore.h>
sem_t sem;

int sem_init(sem_t *sem, int pshared, unsigned int value);
功能:给信号量设置初始值
pshared:信号量的使用范围
	0 线程间使用
	nonzero 进程之间使用
    
int sem_wait(sem_t *sem);
功能:信号量减1操作,如果信号量已经等于0,则阻塞
    
int sem_trywait(sem_t *sem);
功能:尝试对信号量减1操作,能减返回0成功,不能减返回-1失败,不会阻塞
    
int sem_timedwait(sem_t *sem, const struct timespec *abs_timeout);
功能:带倒计时的对信号减1操作,能减返回0成功,不能减超时返回-1失败,阻塞abs_timeout一段时间
    
int sem_post(sem_t *sem);
功能:对信号量执行加1操作
    
int sem_getvalue(sem_t *sem, int *sval);
功能:获取信号量的值
    
int sem_destroy(sem_t *sem);
功能:销毁信号量

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

// 仓库最大容量
#define STACK_MAX 50

// 仓库
char stack[STACK_MAX];
// 仓库的入口和出口
int top;

// 定义信号量
sem_t data_sem,null_sem;

// 显示仓库内容
void show_stack(const char* opt,char data)
{
    
    
    if(50 < top || 0 > top)
    {
    
    
        printf("爆仓了 %d\n",top);
        exit(0);
    }
    for(int i=0; i<top; i++)
    {
    
    
    	printf("%c",stack[i]);
    }
   	 printf("%s%c\n",opt,data);
}

// 生产者
void* production(void* arg)
{
    
    
    for(;;)
    {
    
    
        // 空位置的数量减1
        sem_wait(&null_sem);
        // 生产数据
        char data = rand()%26+'A';
        // 显示仓库
        show_stack("<-",data);
        // 数据添加到仓库
        stack[top] = data;
        __sync_fetch_and_add(&top,1);
        usleep(rand()%100*100);
        // 数量的数量加1
        sem_post(&data_sem);
    }
}

// 消费者
void* consumption(void* arg)
{
    
    
    for(;;)
    {
    
    
        // 数据的数量减1
        sem_wait(&data_sem);
        // 从仓库中消费数据
        char data = stack[top];
        __sync_fetch_and_sub(&top,1);
        // 显示仓库内容
        show_stack("->",data);
        // 模拟消费数据所消耗的时间
        usleep(rand()%1000*10);
        // 空位置的数量加1
        sem_post(&null_sem);
    }
}

int main(int argc,const char* argv[])
{
    
    
    srand(time(NULL));
    
    // 初始化空位置的数量
    sem_init(&null_sem,0,STACK_MAX);
    // 初始化数据的数量
    sem_init(&data_sem,0,0);
    
    pthread_t tids[10];
    for(int i=0; i<10; i++)
    {
    
    
        if(i<5)
       		pthread_create(tids+i,NULL,production,NULL);
        else
       		pthread_create(tids+i,NULL,consumption,NULL);
    }
    
    for(int i=0; i<10; i++)
    {
    
    
        pthread_join(tids[i],NULL);
    }
    
    return 0;
}

Semaphores used by multiple processes:

sem_t *sem_open(const char *name, int oflag,mode_t mode, unsigned int value);
功能:在内核创建一个信号量对象
name:信号量的名字
oflag:
    O_CREAT 不存在则创建信号量,存在则获取
    O_EXCL 如果信号量已经存在,返回失败
mode:信号量的权限
value:信号量的初始值
    
sem_t *sem_open(const char *name, int oflag);
功能:获取信号,或相关属性
    
int sem_unlink(const char *name);
功能:删除信号量

// 进程A
#include <stdio.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <semaphore.h>

int main(int argc,const char* argv[])
{
    
    
    sem_t* sem = sem_open("sem_name",O_CREAT,0644,0);
    for(;;)
    {
    
    
        sem_post(sem);
        sleep(1);
        int value = 0;
        sem_getvalue(sem,&value);
        printf("%d\n",value);
	}
    
	return 0;
}

// 进程B
#include <stdio.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <semaphore.h>

int main(int argc,const char* argv[])
{
    
    
    sem_t* sem = sem_open("sem_name",O_CREAT);
    for(;;)
    {
    
    
        sem_wait(sem);
        sleep(2);
        int value = 0;
        sem_getvalue(sem,&value);
        printf("%d\n",value);
    }
    
    return 0;
}

Guess you like

Origin blog.csdn.net/m0_62480610/article/details/127553447