一、什么是sem?
Semaphore(简称sem)是一种用于多线程同步的计数器,它用于控制对共享资源的访问。sem中的计数器表示可用的资源数量,线程在访问共享资源之前必须先获取sem,获取sem时计数器会减1,释放sem时计数器会加1。如果计数器为0,则表示当前没有可用的资源,线程需要等待其他线程释放sem才能继续执行。
二、如何使用sem?
1. 初始化sem
在使用sem之前需要先对其进行初始化,可以使用sem_init函数进行初始化。sem_init函数的参数包括sem指针、进程共享标志、初始计数器值。
int sem_init(sem_t *sem, int pshared, unsigned int value);
其中sem是指向semaphore的指针,pshared是进程共享标志,如果为0则表示信号量只能在同一进程中的线程间共享,如果为非0则表示信号量可以在多个进程间共享。value是初始计数器值。
2. 获取sem
线程在访问共享资源之前需要获取sem,可以使用sem_wait函数进行获取。sem_wait函数的参数是sem指针。
int sem_wait(sem_t *sem);
如果当前sem的计数器值大于0,则sem_wait会将计数器值减1,并返回0,表示获取sem成功;如果当前sem的计数器值等于0,则sem_wait会阻塞线程,直到其他线程释放sem。
3. 释放sem
线程访问共享资源完成后需要释放sem,可以使用sem_post函数进行释放。sem_post函数的参数是sem指针。
int sem_post(sem_t *sem);
sem_post会将sem的计数器值加1,并返回0,表示释放sem成功。
三、如何使用sem优化操作实现多线程同步?
1. 控制线程数量
在多线程编程中,线程数量的控制非常重要。如果线程数量太多,会导致系统资源的浪费;如果线程数量太少,会导致程序运行效率低下。可以使用sem来控制线程数量,当线程数量达到一定值时,其他线程需要等待。
例如,我们需要下载100个文件,可以开启10个线程同时下载,其他线程需要等待。可以使用如下代码实现:
// 初始化sem,初始值为10
sem_init(&sem, 0, 10);
for (int i = 0; i < 100; i++) {
// 获取sem
sem_wait(&sem);
// 创建线程下载文件
pthread_create(&thread, NULL, download_file, (void*)&file_list[i]);
}
void* download_file(void* arg) {
// 下载文件
// ...
// 释放sem
sem_post(&sem);
}
在下载文件之前,线程需要获取sem;在下载文件完成之后,线程需要释放sem。当sem的计数器值为10时,只有10个线程可以同时下载文件,其他线程需要等待。
2. 实现互斥访问共享资源
在多线程编程中,多个线程可能同时访问共享资源,如果没有进行同步,就会导致数据的错误或不一致。可以使用sem来实现互斥访问共享资源。
例如,我们需要对一个全局变量进行累加操作,可以使用如下代码实现:
// 初始化sem,初始值为1
sem_init(&sem, 0, 1);
void* add(void* arg) {
// 获取sem
sem_wait(&sem);
// 全局变量累加
count++;
// 释放sem
sem_post(&sem);
}
在对全局变量进行累加操作之前,线程需要获取sem;在累加操作完成之后,线程需要释放sem。由于sem的计数器值为1,所以只有一个线程可以访问全局变量,其他线程需要等待。
3. 实现信号通知
在多线程编程中,有时候需要线程之间进行通信,例如线程A需要等待线程B完成某个操作之后才能继续执行。可以使用sem来实现信号通知。
例如,我们需要等待10个线程完成某个操作之后才能继续执行,可以使用如下代码实现:
// 初始化sem,初始值为0
sem_init(&sem, 0, 0);
for (int i = 0; i < 10; i++) {
// 创建线程执行某个操作
pthread_create(&thread, NULL, do_something, (void*)&arg);
}
void* do_something(void* arg) {
// 执行某个操作
// ...
// 释放sem
sem_post(&sem);
}
// 等待10个线程完成某个操作
for (int i = 0; i < 10; i++) {
sem_wait(&sem);
}
在执行某个操作之后,线程需要释放sem;在等待10个线程完成某个操作之前,主线程需要等待sem。由于sem的计数器值为0,所以主线程会一直等待,直到有10个线程释放sem。
四、总结
在多线程编程中,sem是一种非常重要的工具,它可以用于控制线程数量、实现互斥访问共享资源、实现信号通知等。使用sem可以避免多线程编程中的数据竞争等问题,提高程序的运行效率和稳定性。