6. 多线程高级编程
6.1 多线程的同步和异步
- 并行是指两个或多个任务同时执行,实现方式可以是在多个处理器上同时运行或一个处理器上交替执行。简单来说,就是做多个事情。
- 并发是指在一段时间内宏观上有多个程序在同时运行,但是在同一时刻只有一个程序在处理器上运行。简单来说,就是多个程序互相抢占CPU资源,让CPU快速切换,看起来好像是同时执行。
- 多线程的同步和异步是指多个线程之间的协调和合作方式。 在同步模式下,多个线程之间需要相互协作,一个线程的操作需要等待另一个线程的操作完成才能继续执行,在同步模式下,通常会使用锁、条件变量、信号量等机制来实现线程之间的协调和同步,以保证各个线程之间的操作顺序和正确性。 而异步模式下,则多个线程可以独立运行,彼此之间不需要进行协调,一个线程的操作不会受到其他线程的影响。
- 例如,在多线程程序中,多个线程可能同时访问共享数据,此时需要使用锁机制来保证数据的一致性和完整性,避免数据竞争和错误。 又如,在生产者和消费者模式中,生产者线程和消费者线程需要彼此协作,以保证生产和消费的顺序和数量匹配,此时可以使用条件变量和信号量来实现线程之间的协调和同步。 在异步模式下,各个线程可以独立运行,彼此之间不需要进行协调和同步,可以根据需要随时启动、暂停、终止线程,以实现更加灵活的程序控制。 例如,在多线程网络编程中,可以为每个客户端连接启动一个独立的线程来处理数据交换,各个线程之间独立运行,彼此不会影响,可以有效提高程序的并发性和响应速度。
6.2 利用POSIX多线程API函数进行线程的同步
- 对于多线程程序来说,同步是指在一定的时间内只允许某一个线程来访问某个资源。而在此时间内,不允许其他的线程访问该资源。可以通过互斥锁(Mutex)、条件变量(condition variable)、读写锁(reader-writer lock)、信号量(semaphore)来同步资源。
6.2.1 互斥锁
- 互斥锁是线程同步的一种机制,用于保护共享资源免受并发访问的影响。互斥锁只允许一个线程访问共享资源,其他线程必须等待互斥锁被释放后才能访问。锁的获取和释放也称上锁(lock)和解锁(unlock)。
- 在 POSIX 系统中,互斥锁是通过 pthread 库实现的。下面介绍几个常用的互斥锁函数:
-
用于初始化互斥锁的函数是pthread_mutex_init(),函数声明:
#include <pthread.h>int pthread_mutex_init(pthread_mutex_t *mutex, const pthread_mutexattr_t *attr);- 参数说明:
- mutex:指向互斥锁的指针;
- attr:指向互斥锁属性的指针,可以为 NULL。
- 返回值说明:成功时返回0,失败时返回错误码。
- 参数说明:
-
线程初始化后,就可以上锁了,用于上锁的函数是pthread_mutex_lock(),函数声明:
#include <pthread.h>int pthread_mutex_lock(pthread_mutex_t *mutex);- 参数说明:
- mutex:指向互斥锁的指针。
- 返回值说明:成功时返回0,失败时返回错误码。
- 参数说明:
-
加锁和解锁操作必须成对出现,否则会导致死锁或其他问题,用于解锁的函数是pthread_mutex_unlock(),函数声明:
#include <pthread.h>int pthread_mutex_unlock(pthread_mutex_t *mutex);- 参数说明:
- mutex:指向互斥锁的指针。
- 返回值说明:成功时返回0,失败时返回错误码。
- 参数说明:
-
当互斥锁用完后,最终要销毁,用于销毁互斥锁的函数是pthread_mutex_destroy(),函数声明:
#include <pthread.h>int pthread_mutex_destroy(pthread_mutex_t *mutex);- 参数说明:
- mutex:指向互斥锁的指针。
- 返回值说明:成功时返回0,失败时返回错误码。
- 参数说明:
-
使用互斥锁前多线程累加示例:
#include <stdio.h>#include <unistd.h>#include <pthread.h>#include <sys/time.h>#include <string.h>#include <stdlib.h>int n = 0;pthread_mutex_t mutex;void *thread_1(void *arg){int j;for (j = 0; j < 1000000; j++) {n++;}pthread_exit((void *)0);}void *thread_2(void *arg){int j;for (j = 0; j < 1000000; j++) {n++;}pthread_exit((void *)0);}int main(void){int j,err;pthread_t th1, th2;for (j = 0; j < 10; j++){err = pthread_create(&th1, NULL, thread_1, (void *)0);if (err != 0) {printf("create new thread error:%s\n", strerror(err));exit(0);}err = pthread_create(&th2, NULL, thread_2, (void *)0);if (err != 0) {printf("create new thread error:%s\n", strerror(err));exit(0);}err = pthread_join(th1, NULL);if (err != 0) {printf("wait thread done error:%s\n", strerror(err));exit(1);}err = pthread_join(th2, NULL);if (err != 0) {printf("wait thread done error:%s\n", strerror(err));exit(1);}printf("n=%d\n", n);n = 0;}return 0;} -
使用互斥锁后多线程累加示例:
#include <stdio.h>#include <unistd.h>#include <pthread.h>#include <sys/time.h>#include <string.h>#include <stdlib.h>int n = 0;pthread_mutex_t mutex;void *thread_1(void *arg){int j;for (j = 0; j < 1000000; j++){pthread_mutex_lock(&mutex);n++;pthread_mutex_unlock(&mutex);}pthread_exit((void *)0);}void *thread_2(void *arg){int j;for (j = 0; j < 1000000; j++){pthread_mutex_lock(&mutex);n++;pthread_mutex_unlock(&mutex); //解锁}pthread_exit((void *)0);}int main(void){int j,err;pthread_t th1, th2;pthread_mutex_init(&mutex, NULL); //初始化互斥锁for (j = 0; j < 10; j++){err = pthread_create(&th1, NULL, thread_1, (void *)0);if (err != 0) {printf("create new thread error:%s\n", strerror(err));exit(0);}err = pthread_create(&th2, NULL, thread_2, (void *)0);if (err != 0) {printf("create new thread error:%s\n", strerror(err));exit(0);}err = pthread_join(th1, NULL);if (err != 0) {printf("wait thread done error:%s\n", strerror(err));exit(1);}err = pthread_join(th2, NULL);if (err != 0) {printf("wait thread done error:%s\n", strerror(err));exit(1);}printf("n=%d\n", n);n = 0;}pthread_mutex_destroy(&mutex); //销毁互斥锁return 0;} -
上述两个程序的含义都是创建两个线程,都是将n累加1000000次,最终的输出结果应为2000000,实际上由于线程间产生了资源竞争,未加互斥锁的程序的输出结果无法累加到2000000。
6.2.2 读写锁
- 读写锁是一种特殊的锁,可以提高并发程序中对于共享资源的读操作效率。读写锁分为读锁和写锁两种,读锁可被多个线程同时持有,但是写锁只能被单个线程持有。当某个线程占用写锁时,其他线程无法持有读锁或写锁,这样可以保证数据的一致性。读写锁适用于多读少写的场景,因为它可以允许多个线程同时读取共享资源,而不会因为读操作的阻塞而影响程序性能。
-
在 POSIX 线程库中,初始化读写锁使用的函数是pthread_rwlock_init(),函数声明:
#include <pthread.h>int pthread_rwlock_init(pthread_rwlock_t *rwlock, const pthread_rwlockattr_t *attr)- 参数含义:
- rwlock:指向 pthread_rwlock_t 结构体的指针,即需要被初始化的读写锁;
- attr:指向 pthread_rwlockattr_t 结构体的指针,表示读写锁的属性。如果该参数为 NULL,则使用默认属性。
- 返回值:如果函数执行成功就返回0,否则返回错误码。
- 参数含义:
-
读写锁的上锁可分为读模式下的上锁和写模式下的上锁,函数声明:
#include <pthread.h>int pthread_rwlock_rdlock(pthread_rwlock_t *rwlock)- 参数含义:
- rwlock:指向 pthread_rwlock_t 结构体的指针,即需要被加锁的读写锁。
- 返回值:如果获取读锁成功,函数返回值为 0,否则返回错误码。
#include <pthread.h>int pthread_rwlock_tryrdlock(pthread_rwlock_t *rwlock)- 参数含义:
- rwlock:指向 pthread_rwlock_t 结构体的指针,即需要被加锁的读写锁。
- 返回值:如果获取读锁成功,函数返回值为 0,否则返回错误码。
- 使用 pthread_rwlock_tryrdlock() 函数尝试获取读锁时,如果有其他线程已经获取了写锁(互斥锁),则当前线程尝试获取读锁会失败,函数将立即返回失败。
#include <pthread.h>int pthread_rwlock_wrlock(pthread_rwlock_t *rwlock)- 参数含义:
- rwlock:指向 pthread_rwlock_t 结构体的指针,即需要被加锁的读写锁。
- 函数执行成功后,当前线程将持有写锁(独占锁),其他线程将无法同时持有读锁或写锁,直到当前线程释放写锁。
- 返回值:如果获取写锁成功,函数返回值为 0,否则返回错误码。
#include <pthread.h>int pthread_rwlock_trywrlock(pthread_rwlock_t *rwlock)- 参数含义:
- rwlock:指向 pthread_rwlock_t 结构体的指针,即需要被加锁的读写锁。
- 函数执行成功后,当前线程将持有写锁(独占锁),其他线程将无法同时持有读锁或写锁,直到当前线程释放写锁。
- pthread_rwlock_trywrlock() 函数用于尝试获取读写锁的写锁(独占锁),如果当前读写锁已经被加了读锁或写锁,则函数将立即返回失败。其参数含义如下:
- 返回值:如果获取写锁成功,函数返回值为 0,否则返回错误码。
- 参数含义:
-
当线程退出临界区后,要对读写锁进行解锁,解锁的函数是pthread_rwlock_unlock(),函数声明:
#include <pthread.h>int pthread_rwlock_unlock(pthread_rwlock_t *rwlock)- 参数含义:
- rwlock:指向 pthread_rwlock_t 结构体的指针,即需要被解锁的读写锁。
- 函数执行成功后,当前线程将释放读锁或写锁,其他线程将可以持有读锁或写锁。
- 参数含义:
-
当读写锁用完后,最终需要销毁,用于销毁读写锁的函数是pthread_rwlock_destroy(),函数声明:
#include <pthread.h>int pthread_rwlock_destroy(pthread_rwlock_t *rwlock)- 参数含义:
- rwlock:指向 pthread_rwlock_t 结构体的指针,即需要被销毁的读写锁。
- 函数执行成功后,读写锁将被销毁,释放相关的资源。
- 参数含义:
-
模拟一个共享变量的读写过程:
#include <pthread.h>#include <stdio.h>#include <stdlib.h>#include <unistd.h>#define NUM_THREADS 3#define NUM_ITERATIONS 10int shared_data = 0; // 共享数据pthread_rwlock_t rwlock; // 读写锁void *reader(void *arg){for (int i = 0; i < NUM_ITERATIONS; ++i){pthread_rwlock_rdlock(&rwlock);printf("Reader %ld: read shared_data = %d\n", (long int) arg, shared_data);pthread_rwlock_unlock(&rwlock);usleep(rand() % 100000); // 模拟读操作耗时}pthread_exit(NULL);}void *writer(void *arg){for (int i = 0; i < NUM_ITERATIONS; ++i){pthread_rwlock_wrlock(&rwlock);shared_data++;printf("Writer %ld: write shared_data = %d\n", (long int) arg, shared_data);pthread_rwlock_unlock(&rwlock);usleep(rand() % 100000); // 模拟写操作耗时}pthread_exit(NULL);}int main() {pthread_t threads[NUM_THREADS];pthread_rwlock_init(&rwlock, NULL);// 创建读线程和写线程for (intptr_t i = 0; i < NUM_THREADS; ++i) {if (i == 0) {pthread_create(&threads[i], NULL, writer, (void *) i);} else {pthread_create(&threads[i], NULL, reader, (void *) i);}}// 等待线程结束for (int i = 0; i < NUM_THREADS; ++i) {pthread_join(threads[i], NULL);}pthread_rwlock_destroy(&rwlock);return 0;}
6.2.3 条件变量
-
条件变量(Condition Variable)是一种用于线程同步的机制。它允许线程等待某个条件的发生,当条件不满足时,线程会被阻塞,直到另一个线程通知该条件发生的变化, 从而使得阻塞的线程可以继续执行。条件变量通常和互斥锁(Mutex)一起使用。当一个线程需要等待某个条件的发生时,它会先获取互斥锁,然后判断条件是否满足, 如果不满足,则通过条件变量进入等待状态,等待其他线程通知该条件的发生变化。当条件发生变化时,通知等待的线程即可。
-
条件变量由两个主要操作组成:wait 和 signal。wait 操作用于等待信号,如果条件不满足则线程会在条件变量上等待。signal 操作用于发出信号,通知等待线程条件已经满足,可以继续执行了。 在使用条件变量时,通常需要与互斥锁配合使用。当等待线程收到信号时,它会重新获得互斥锁,并检查条件是否满足,然后继续执行。因此,条件变量的使用通常包含以下几个步骤:
1). 初始化条件变量和互斥锁 2). 在等待线程中,获取互斥锁,检查条件是否满足,如果不满足,则使用条件变量等待信号 3). 在发出信号的线程中,获取互斥锁,更改条件状态,然后使用条件变量发出信号 4). 在等待线程中,收到信号后,重新获取互斥锁,检查条件是否满足,如果满足,则继续执行
-
需要注意的是,在使用条件变量时,必须先获得互斥锁,再使用条件变量等待或发出信号,否则会导致竞态条件的发生。同时,使用条件变量时还需要处理虚假唤醒的情况,即线程在等待条件变量时可能会被意外唤醒,因此等待线程需要再次检查条件是否满足。
-
条件变量可以通过静态初始化和函数初始化两种方式来创建和初始化。
- 静态初始化方式适用于条件变量在定义时即进行初始化的情况,使用 PTHREAD_COND_INITIALIZER 宏来初始化一个条件变量,该宏会生成一个 pthread_cond_t 类型的静态变量并初始化为默认值,代码如下:
#include <pthread.h>int pthread_cond_t cond = PTHREAD_COND_INITIALIZER;- 函数初始化方式适用于在运行时动态创建条件变量的情况,需要使用 pthread_cond_init 函数进行初始化,该函数的原型为:
#include <pthread.h>int pthread_cond_init(pthread_cond_t *cond, const pthread_condattr_t *attr);- cond 参数是指向条件变量对象的指针,attr 参数是指向条件变量属性的指针,通常设置为 NULL。
-
等待条件变量。在使用条件变量时,通常需要等待某个条件满足才能继续执行,这时可以使用pthread_cond_wait()函数等待条件变量的通知。函数声明:
#include <pthread.h>int pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex);该函数将会阻塞当前线程,直到有其他线程调用pthread_cond_signal或pthread_cond_broadcast通知条件变量。 在等待前,需要获取mutex锁,并在等待过程中将mutex锁释放,以便其他线程可以修改共享状态。 当等待结束后,该函数会自动获取mutex锁并返回。此时可以在获取mutex锁后,检查条件是否满足并继续执行。 需要注意的是,pthread_cond_wait函数可能会发生虚假唤醒,即使条件变量没有被通知也会返回。
因此,在等待条件变量时,通常需要将等待过程放在一个循环中,并检查条件是否满足,以避免虚假唤醒导致的错误。 另外,使用pthread_cond_wait函数前,还需要确保mutex锁已经被获取,否则该函数将会产生未定义的行为。因此,一般建议使用如下的方式等待条件变量:
pthread_mutex_lock(&mutex);while (!condition_is_satisfied()){pthread_cond_wait(&cond, &mutex);}// 这里条件满足,可以继续执行pthread_mutex_unlock(&mutex); -
唤醒等待条件变量的线程。可以使用 pthread_cond_signal() 或 pthread_cond_broadcast() 函数来唤醒等待条件变量的线程,函数声明:
#include <pthread.h>int pthread_cond_signal(pthread_cond_t *cond);int pthread_cond_broadcast(pthread_cond_t *cond);- 参数含义: cond 是指向条件变量对象的指针。
- 返回值:成功返回 0,失败返回一个正整数的错误代码。
- 注意:这两个函数都需要先获得相应的互斥锁,以避免发生竞争条件。pthread_cond_signal 函数会唤醒等待在条件变量上的某一个线程(如果有的话),而 pthread_cond_broadcast 函数会唤醒等待在条件变量上的所有线程。
-
条件变量的销毁。条件变量的销毁函数是pthread_cond_destroy,函数声明:
#include <pthread.h>int pthread_cond_destroy(pthread_cond_t *cond);- 参数:cond是指向要销毁的条件变量的指针。
- 该函数用于销毁条件变量,释放与其关联的资源。在使用pthread_cond_t类型的条件变量后,应该在不需要使用它时,调用该函数进行销毁。销毁条件变量时,应该保证所有与其关联的线程已经退出并不再使用该条件变量。
-
找出1~20中能整除3的整数:
#include <pthread.h>#include <stdio.h>#include <stdlib.h>#include <unistd.h>pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;/*初始化互斥锁*/pthread_cond_t cond = PTHREAD_COND_INITIALIZER;/*初始化条件变量*/void *thread1(void *);void *thread2(void *);int i = 1;int main(void){pthread_t t_a;pthread_t t_b;pthread_create(&t_a, NULL, thread2, (void *)NULL);//创建线程t_apthread_create(&t_b, NULL, thread1, (void *)NULL); //创建线程t_bpthread_join(t_b, NULL);/*等待进程t_b结束*/pthread_mutex_destroy(&mutex);pthread_cond_destroy(&cond);exit(0);}void *thread1(void *junk){for (i = 1; i <= 20; i++){pthread_mutex_lock(&mutex);//锁住互斥量if (i % 3 == 0)pthread_cond_signal(&cond); //唤醒等待条件变量cond的线程elseprintf("thead1:%d\n", i); //打印不能整除3的ipthread_mutex_unlock(&mutex);//解锁互斥量sleep(1);}}void *thread2(void *junk){while (i < 20){pthread_mutex_lock(&mutex);if (i % 3 != 0)pthread_cond_wait(&cond, &mutex);//等待条件变量printf("------------thread2:%d\n", i); //打印能整除3的ipthread_mutex_unlock(&mutex);sleep(1);i++;}}
-
编译运行程序
linaro@linaro-alip:~/Linux/thread$ gcc -o Condition_Variable Condition_Variable.c -lpthreadlinaro@linaro-alip:~/Linux/thread$ ./Condition_Variablethead1:1thead1:2------------thread2:3thead1:4------------thread2:6thead1:7------------thread2:9thead1:10------------thread2:12thead1:13------------thread2:15thead1:16------------thread2:18thead1:19linaro@linaro-alip:~/Linux/thread$
6.2.4 信号量
-
POSIX信号量是一种用于线程同步和互斥的机制,它提供了一组函数和数据类型,用于实现同步和互斥访问共享资源的操作。它分为有名信号量和无名信号量两种类型。
- 有名信号量:适用于不同进程间的通信和同步,通过唯一的名称在系统中标识,多个进程可以访问同一个有名信号量。
- 无名信号量:适用于同一个进程内的线程之间同步,无名信号量只可以在共享内存的情况下,比如实现进程中各个线程之间的互斥和同步,因此无名信号量也被称作基于内存的信号量。
-
POSIX 信号量与 System V 信号量的区别如下:
- 对 POSIX 来说,信号量是个非负整数,常用于线程间同步。而 System V 信号量则是一个或多个信号量的集合,它对应的是一个信号量结构体,这个结构体是为 System V IPC 服务的,信号量只不过是它的一部分,常用于进程间同步。
- POSIX 信号量的引用头文件是
<semaphore.h>,而 System V 信号量的引用头文件是<sys/sem.h>。 - 从使用的角度,System V 信号量的使用比较复杂,而 POSIX 信号量使用起来相对简单。
无名信号量
-
初始化一个无名的信号量,使用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:指向未命名信号量的指针。
-
返回值说明:成功时返回0,失败时返回-1。
-
-
等待未命名信号量,如果信号量的计数器大于0,则将其减1。如果计数器的值为0,则阻塞线程或进程,直到有其他线程或进程增加信号量的计数器。使用函数sem_wait(),函数声明:
#include <semaphore.h>int sem_wait(sem_t *sem);-
参数说明:sem:指向未命名信号量的指针。
-
返回值说明:成功时返回0,失败时返回-1。
-
-
增加未命名信号量的计数器的值,并唤醒阻塞在该信号量上等待的线程或进程,使用函数sem_post(),函数声明:
#include <semaphore.h>int sem_post(sem_t *sem);-
参数说明:sem:指向未命名信号量的指针。
-
返回值说明:成功时返回0,失败时返回-1。
-
-
无名信号量应用实例:
#include <stdio.h>#include <pthread.h>#include <semaphore.h>#include <unistd.h>#define NUM_THREADS 3sem_t semaphore;void* threadFunction(void* arg){int threadId = *((int*)arg);printf("Thread %d is waiting.\n", threadId);sem_wait(&semaphore); // 等待信号量,如果信号量值大于0则继续执行,否则阻塞printf("Thread %d acquired the semaphore.\n", threadId);// 模拟线程的工作printf("Thread %d is working...\n", threadId);sleep(2);printf("Thread %d released the semaphore.\n", threadId);sem_post(&semaphore); // 发信号,增加信号量的值pthread_exit(NULL);}int main(){pthread_t threads[NUM_THREADS];int threadIds[NUM_THREADS];sem_init(&semaphore, 0, 1); // 初始化信号量,初始值为1for (int i = 0; i < NUM_THREADS; i++){threadIds[i] = i;pthread_create(&threads[i], NULL, threadFunction, &threadIds[i]);}for (int i = 0; i < NUM_THREADS; i++){pthread_join(threads[i], NULL);}sem_destroy(&semaphore); // 销毁信号量return 0;}- 在这个示例程序中,有三个线程同时运行,它们通过无名信号量实现了对临界资源的互斥访问。每个线程都等待获取信号量,然后执行一段工作,完成后释放信号量,让其他线程获取。无名信号量的初始值为1,表示只有一个线程可以获取信号量,其他线程需要等待。通过这种方式,线程之间实现了同步和互斥的访问。
-
编译运行程序,输出结果:
linaro@linaro-alip:~/Linux/thread$ gcc -o posix_sem posix_sem.c -lpthreadlinaro@linaro-alip:~/Linux/thread$ ./posix_semThread 1 is waiting.Thread 1 acquired the semaphore.Thread 1 is working...Thread 0 is waiting.Thread 2 is waiting.Thread 1 released the semaphore.Thread 0 acquired the semaphore.Thread 0 is working...Thread 0 released the semaphore.Thread 2 acquired the semaphore.Thread 2 is working...Thread 2 released the semaphore.linaro@linaro-alip:~/Linux/thread$
命名信号量
-
打开或创建一个命名信号量使用sem_open()函数,函数声明:
#include <semaphore.h>sem_t *sem_open(const char *name, int oflag, mode_t mode, unsigned int value);-
参数说明:
- name:指定信号量的名称。
- oflag:指定打开或创建信号量的标志。
- mode:指定创建新信号量时的权限掩码。
- value:指定信号量的初始计数器的值。
-
返回值说明:成功时返回指向命名信号量的指针,失败时返回
SEM_FAILED。
-
-
使用sem_close()函数来关闭命名信号量,函数声明:
#include <semaphore.h>int sem_close(sem_t *sem);-
参数说明:sem:指向命名信号量的指针。
-
返回值说明:成功时返回0,失败时返回-1。
-
-
在所有进程关闭了命名信号量之后,使用sem_unlink()函数将信号量从系统中删除,函数声明:
#include <semaphore.h>int sem_unlink(const char *name); -
命名信号量应用实例:
#include <stdio.h>#include <stdlib.h>#include <fcntl.h>#include <sys/stat.h>#include <semaphore.h>#include <unistd.h>#include <sys/wait.h>int main(){sem_t* semaphore;const char* semaphoreName = "/my_named_semaphore";int childPid;// 创建有名信号量semaphore = sem_open(semaphoreName, O_CREAT | O_EXCL, 0644, 1);if (semaphore == SEM_FAILED){perror("sem_open");exit(EXIT_FAILURE);}// 创建子进程childPid = fork();if (childPid == -1){perror("fork");exit(EXIT_FAILURE);}if (childPid == 0){// 子进程// 打开已存在的有名信号量semaphore = sem_open(semaphoreName, 0);if (semaphore == SEM_FAILED){perror("sem_open");exit(EXIT_FAILURE);}// 获取信号量printf("Child process is waiting.\n");sem_wait(semaphore);// 执行临界区代码printf("Child process acquired the semaphore.\n");printf("Child process is working...\n");sleep(2);// 释放信号量printf("Child process released the semaphore.\n");sem_post(semaphore);// 关闭信号量sem_close(semaphore);exit(EXIT_SUCCESS);}else{// 父进程// 获取信号量printf("Parent process is waiting.\n");sem_wait(semaphore);// 执行临界区代码printf("Parent process acquired the semaphore.\n");printf("Parent process is working...\n");sleep(2);// 释放信号量printf("Parent process released the semaphore.\n");sem_post(semaphore);// 等待子进程结束wait(NULL);// 关闭并销毁信号量sem_close(semaphore);sem_unlink(semaphoreName);}return 0;}- 在这个示例程序中,父进程和子进程共享一个有名信号量。父进程先创建有名信号量并获取它,执行一段临界区代码后释放信号量。接着,父进程创建子进程,子进程打开已存在的有名信号量并获取它,执行一段临界区代码后释放信号量。通过有名信号量的使用,父子进程可以实现对临界资源的同步和互斥访问。
-
编译运行程序,输出结果:
linaro@linaro-alip:~/Linux/thread$ gcc -o posix_name_sem posix_name_sem.c -lpthreadlinaro@linaro-alip:~/Linux/thread$ ./posix_name_semParent process is waiting.Parent process acquired the semaphore.Parent process is working...Child process is waiting.Parent process released the semaphore.Child process acquired the semaphore.Child process is working...Child process released the semaphore.linaro@linaro-alip:~/Linux/thread$