c-只有一个线程获取信号量



我有一个程序,其中多个线程处于一个循环中,它们获取一个二进制信号量,然后增加一个全局计数器。但是,通过打印出线程ID,我注意到只有一个线程获取信号量。这是我的MRE:

#include <stdbool.h>
#include <stdio.h>
#include <unistd.h>
#include <pthread.h>
#include <semaphore.h>
#define NUM_THREADS 10
#define MAX_COUNTER 100
struct threadCtx {
sem_t sem;
unsigned int counter;
};
static void *
threadFunc(void *args)
{
struct threadCtx *ctx = args;
pthread_t self;
bool done = false;
self = pthread_self();
while (!done) {
sem_wait(&ctx->sem);
if ( ctx->counter == MAX_COUNTER ) {
done = true;
}
else {
sleep(1);
printf("Thread %u increasing the counter to %un", (unsigned int)self, ++ctx->counter);
}
sem_post(&ctx->sem);
}
return NULL;
}
int main() {
pthread_t threads[NUM_THREADS];
struct threadCtx ctx = {.counter = 0};
sem_init(&sem.ctx, 0, 1);
for (int k=0; k<NUM_THREADS; k++) {
pthread_create(threads+k, NULL, threadFunc, &ctx);
}
for (int k=0; k<NUM_THREADS; k++) {
pthread_join(threads[k], NULL);
}
sem_destroy(&ctx.sem);
return 0;
}

输出为

Thread 1004766976 increasing the counter to 1
Thread 1004766976 increasing the counter to 2
Thread 1004766976 increasing the counter to 3
...

如果我删除对sleep的调用,则行为更接近于我所期望的(即,线程以看似不确定的方式被唤醒(。为什么会这样?

David Schwartz的回答解释了在低水平上发生的事情。也就是说,他是从操作系统开发人员或硬件设计师的角度来看待它的。这没什么错,但让我们从软件架构师的角度来看你的程序:

您有多个线程都在执行同一个循环。循环锁定互斥对象;工作;然后它释放互斥。好吧,但它接下来要做什么?几乎您的循环在释放互斥对象后所做的下一件事就是再次锁定互斥对象。你的循环几乎100%的时间都在做";工作;互斥锁被锁定。

那么,当从来没有机会让两个或多个线程同时工作时,在多个线程中运行同一个循环有什么意义呢?

如果你想使用线程进行并行计算,你需要找到/发明安全的方法,让线程在互斥解锁的情况下完成大部分工作它们应该只锁定互斥对象足够长的时间来发布结果或进行另一次赋值。

有时,这意味着编写的代码的效率不如单线程代码。但假设程序(A(有一个几乎100%使用CPU的单线程,而程序(B(使用八个CPU,但仅使用50%的效率。哪个项目将获胜?


*我知道,您的示例使用了一个sem_t(信号量(对象。但是";信号量";是您正在使用的"Mutex";是您使用它的角色

为什么会这样?

上下文开关成本高昂,而且您的实现明智地将其最小化。您的线程都在争夺同一资源,试图密切调度它们会使性能变得更差,可能会影响整个系统。

由于不断获取信号量的线程永远不会用完它的时间片,所以它将不断获取资源。编写代码来完成您想要完成的工作是您的责任。实现的责任是尽可能高效地执行代码,这就是它所做的。

最有可能的是,幕后的情况是:

  1. 不断获取信号的线程总是可以向前推进,除非它在睡觉。但当它处于睡眠状态时,没有其他需要信号的线程可以向前推进。

  2. 不断获取信号量的线程永远不会耗尽它的时间片,因为它会在这之前休眠。

因此,除了在睡眠时,实现没有任何理由阻塞这个线程,这意味着没有其他线程可以获得信号量。如果您不想让这个线程继续使用信号量并阻塞其他线程,那么就编写不同的代码。

最新更新