我正在尝试理解信号量的语义。
这是一些代码:
#include <semaphore.h>
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <unistd.h>
sem_t sem;
void *helper(void *arg) {
sem_wait(&sem);
printf("helper woke up\n");
}
int main() {
sem_init(&sem, 0, 0);
pthread_t tid;
pthread_create(&tid, NULL, helper, NULL);
sleep(1);
sem_post(&sem);
sem_wait(&sem);
printf("main woke up\n");
exit(0);
}
这里 main 创建一个辅助线程,休眠一秒钟以(几乎)确定辅助线程已运行并等待信号量,然后尝试快速连续地发布并等待信号量。根据 POSIX (https://pubs.opengroup.org/onlinepubs/009695399/functions/sem_post.html):
如果此操作产生的信号量值为零,则应允许阻塞等待信号量的线程之一从其对
的调用中成功返回。sem_wait()
所以,我希望打印“助手醒来”。但是,如果您实际上在 Linux 上运行此代码,通常会观察到“main 唤醒”,这意味着默认情况下 Linux 上的信号量是允许不公平的。
我的问题是:为什么允许这种行为?是的,你可以说“它更容易实现”,但这并不能解释为什么信号量是用这些特定的语义设计的。
在其他地方,(https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/Semaphore.html)它表示这种行为在某些情况下更可取。在什么情况下非公平信号量更好?我将不胜感激有关此主题的任何详细资料。
这样做的一个更好的方法是它减少了上下文切换。允许调用
sem_post()
的线程继续运行,而不必恢复在 sem_wait()
中阻塞的线程。
当调度程序决定解除哪个线程的阻塞时,主线程已调用
sem_wait()
。然后它选择要解除封锁的服务员。然后,它可以根据调度优先级(如引用段落的其余部分所述)或任意选择。并且它可能会选择主线程以避免上下文切换。