Linux多线程编程是现代操作系统最基本、也是最重要的部分之一。在实际应用开发中,多线程编程可以优化程序的性能,提高代码运行效率。本文将详细介绍如何通过信号量实现线程之间的通信,包含完整的代码示例。
一、什么是信号量?
信号量是一种用于多线程同步互斥的机制,用来协调进程对共享资源的访问。信号量是一个计数器,用来记录一个共享资源的数量,当某个进程需要使用该资源时,它需要先获取该资源对应的信号量,获得信号量后才可以访问该资源,当进程结束对该资源的使用后,它将释放该资源对应的信号量。
二、线程的通信方式
线程间的通信方式有很多种,如共享内存、管道、消息队列等。本文将重点介绍基于信号量的线程通信方式。
线程通信方式的基本思路如下:
-
线程A通过获取信号量S1来获得对资源R1的访问权。
-
线程B通过获取信号量S2来获得对资源R2的访问权。
-
线程A获取到S1信号量后,开始对资源R1进行访问操作。
-
线程B获取到S2信号量后,开始对资源R2进行访问操作。
-
当线程A访问操作完成后,释放S1信号量;
-
当线程B访问操作完成后,释放S2信号量。
这样就实现了线程之间的通信。
三、线程通过信号量实现通信的代码实现
下面是基于信号量的线程通信代码实现,包含两个示例:
示例1:生产者-消费者模型
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <semaphore.h>
#define BUFF_SIZE 5
sem_t mutex, full, empty;
int buffer[BUFF_SIZE];
int iHead = 0, iTail = 0;
void* producer(void* arg) {
int item;
while(1) {
item = rand()%10;
sem_wait(&empty);
sem_wait(&mutex);
buffer[iTail] = item;
printf("producer produce item: %d\n", item);
iTail = (iTail+1) % BUFF_SIZE;
sem_post(&mutex);
sem_post(&full);
sleep(1);
}
}
void* consumer(void* arg) {
int item;
while(1) {
sem_wait(&full);
sem_wait(&mutex);
item = buffer[iHead];
printf("consumer consume item: %d\n", item);
iHead = (iHead+1) % BUFF_SIZE;
sem_post(&mutex);
sem_post(&empty);
sleep(1);
}
}
int main() {
pthread_t tid1, tid2;
sem_init(&mutex, 0, 1);
sem_init(&full, 0, 0);
sem_init(&empty, 0, BUFF_SIZE);
pthread_create(&tid1, NULL, producer, NULL);
pthread_create(&tid2, NULL, consumer, NULL);
pthread_join(tid1, NULL);
pthread_join(tid2, NULL);
sem_destroy(&mutex);
sem_destroy(&full);
sem_destroy(&empty);
return 0;
}
代码中,生产者线程通过sem_wait函数降低empty信号量的值,意味着资源队列中增加了一个资源;然后通过sem_wait函数降低mutex信号量的值,意味着其获得了对资源队列的独占访问权;将产生的资源添加到队列中,按照队列FIFO(先进先出)的原则,采用循环数组的方式对队列进行更新,将iTial指针向后移动;最后通过sem_post函数增加mutex信号量和full信号量的值,表示生产者完成生产,生产出的资源能够被消费者使用。
消费者线程通过sem_wait函数降低full信号量的值,意味着资源已经被生产者放置在了队列中;由于full>=1,因此才执行下面的sem_wait函数降低mutex信号量的值,表示对资源的独占使用权;消费者从共享队列中获取一个资源后,将队列的头指针向后移动,由于消耗了一个资源,通过sem_post函数增加mutex信号量和empty信号量的值,表示消费者完成了资源的消耗。
示例2:读者-写者模型
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <semaphore.h>
#define READ_THREAD_NUM 5
#define WRITE_THREAD_NUM 2
sem_t rwt, readMutex, writeMutex;
int data = 0;
int readerCount = 0;
void* reader(void* arg) {
int id = *(int*)arg;
while(1) {
sem_wait(&rwt); //请求对资源的读取
sem_wait(&readMutex); //请求对读取者数量的独占访问
readerCount++;
if(readerCount == 1) {
sem_wait(&writeMutex); //请求对资源的独占访问
}
sem_post(&readMutex); //放弃对读取者数量的独占访问
sem_post(&rwt); //放弃对资源的读取
printf("reader %d read data %d\n", id, data);
sem_wait(&readMutex);
readerCount--;
if(readerCount == 0) {
sem_post(&writeMutex); //放弃对资源的独占访问
}
sem_post(&readMutex);
sleep(1);
}
}
void* writer(void* arg) {
int id = *(int*)arg;
while(1) {
sem_wait(&writeMutex); //请求对资源的独占访问
data++;
printf("writer %d write data %d\n", id, data);
sem_post(&writeMutex); //放弃对资源的独占访问
sleep(1);
}
}
int main() {
pthread_t readTid[READ_THREAD_NUM], writeTid[WRITE_THREAD_NUM];
sem_init(&rwt, 0, 1);
sem_init(&readMutex, 0, 1);
sem_init(&writeMutex, 0, 1);
int id[READ_THREAD_NUM+WRITE_THREAD_NUM];
for(int i=0; i<READ_THREAD_NUM; i++) {
id[i] = i;
pthread_create(&readTid[i], NULL, reader, (void*)&id[i]);
}
for(int i=0; i<WRITE_THREAD_NUM; i++) {
id[i+READ_THREAD_NUM] = i;
pthread_create(&writeTid[i], NULL, writer, (void*)&id[i+READ_THREAD_NUM]);
}
for(int i=0; i<READ_THREAD_NUM; i++) {
pthread_join(readTid[i], NULL);
}
for(int i=0; i<WRITE_THREAD_NUM; i++) {
pthread_join(writeTid[i], NULL);
}
sem_destroy(&rwt);
sem_destroy(&readMutex);
sem_destroy(&writeMutex);
return 0;
}
代码中,读者线程使用sem_wait函数对rwt进行降值操作, 获得对共享资源的读取权力;同时请求对readMutex进行独占访问,用来更新读取者的个数;如果读取者个数为1,则请求对共享资源的独占访问权(通过sem_wait函数),此时就可以阻止写者使用共享资源,防止产生冲突并保护数据一致性。获得共享资源的读取权力和读取个数的独占访问权后,该线程开始进行读取共享变量的操作,读取完成后释放对readMutex的独占访问权,同时请求对readMutex进行降值操作,用来更新读取者的个数;如果当前读取者个数为0,则表示一定没有其他读者在使用共享资源,通过sem_post函数释放对共享资源的独占访问权限(此说明中的独占访问权限类似于进程的 PV 操作)。写者线程同理。
以上就是无锁多线程编程中,通过信号量实现线程之间的通信方式的完整攻略。
本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:linux多线程编程详解教程(线程通过信号量实现通信代码) - Python技术站