多线程之信号量 Semaphore
当涉及到并发处理时,有许多并发模式可以用来解决问题。以下是一些常见的并发模式:
互斥锁(Mutex):用于保护共享资源,只允许一个线程访问资源,其他线程必须等待。
读写锁(Read-Write Lock):允许多个线程同时读取共享资源,但只允许一个线程写入共享资源。
信号量(Semaphore):用于控制对共享资源的访问数量,可以用来实现线程间的同步。
条件变量(Condition Variable):用于在线程之间进行通信和同步,一个线程可以等待某个条件满足后再继续执行。
线程池(Thread Pool):通过预先创建一组线程来处理任务,避免线程创建和销毁的开销。
生产者-消费者模式(Producer-Consumer Pattern):用于解决生产者和消费者之间的同步问题,确保生产者不会在队列已满时继续生产,消费者不会在队列为空时继续消费。
C语言中的 Semaphore 信号量类型为 sem_t,类型及相关操作定义在头文件 semaphore.h 中,
int sem_init(sem_t *sem, int pshared, unsigned int value); // 创建信号量
int sem_post(sem_t *sem); // 信号量的值加 1
int sem_wait(sem_t *sem); // 信号量的值减 1
int sem_destroy(sem_t *sem); // 信号量销毁
例:
你总共有三种类型的下载任务(类型 id 为 1、2、3),每次从键盘读取一种类型的任务进行下载,但是 CPU 最多可以同时执行 2 个下载任务(创建两个线程)。
#include <stdio.h>
#include <pthread.h>
#include <unistd.h>
#include <semaphore.h>
#define MAXNUM (2)
sem_t semDownload;
pthread_t a_thread, b_thread, c_thread;
int g_phreadNum = 1;
void *func1(void *arg)
{
// 等待信号量的值 > 0
sem_wait(&semDownload);
printf("============== Downloading taskType 1 ============== \n");
sleep(5);
printf("============== Finished taskType 1 ============== \n");
g_phreadNum--;
// 等待线程结束
pthread_join(a_thread, NULL);
}
void *func2(void *arg)
{
sem_wait(&semDownload);
printf("============== Downloading taskType 2 ============== \n");
sleep(3);
printf("============== Finished taskType 2 ============== \n");
g_phreadNum--;
pthread_join(b_thread, NULL);
}
void *func3(void *arg)
{
sem_wait(&semDownload);
printf("============== Downloading taskType 3 ============== \n");
sleep(1);
printf("============== Finished taskType 3 ============== \n");
g_phreadNum--;
pthread_join(c_thread, NULL);
}
int main()
{
// 初始化信号量
sem_init(&semDownload, 0, 0);
int taskTypeId;
while (scanf("%d", &taskTypeId) != EOF)
{
// 输入 0, 测试程序是否能正常退出
if (taskTypeId == 0 && g_phreadNum <= 1)
{
break;
} else if (taskTypeId == 0)
{
printf("Can not quit, current running thread num is %d\n", g_phreadNum - 1);
}
printf("your choose Downloading taskType %d\n", taskTypeId);
// 线程数超过 2 个则不下载
if (g_phreadNum > MAXNUM)
{
printf("!!! You've reached the max number of threads !!!\n");
continue;
}
// 用户选择下载 Task
switch (taskTypeId)
{
case 1:
// 创建线程 1
pthread_create(&a_thread, NULL, func1, NULL);
// 信号量 + 1,进而触发 func1 的任务
sem_post(&semDownload);
// 总线程数 + 1
g_phreadNum++;
break;
case 2:
pthread_create(&b_thread, NULL, func2, NULL);
sem_post(&semDownload);
g_phreadNum++;
break;
case 3:
pthread_create(&c_thread, NULL, func3, NULL);
sem_post(&semDownload);
g_phreadNum++;
break;
default:
printf("!!! error taskTypeId %d !!!\n", taskTypeId);
break;
}
}
// 销毁信号量
sem_destroy(&semDownload);
return 0;
}