【发布时间】:2021-09-30 01:18:23
【问题描述】:
我有一个程序,其中多个线程在一个循环中,它们获取一个二进制信号量,然后增加一个全局计数器。但是,通过打印线程 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 %u\n", (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 的调用,则行为更接近我的预期(即,线程以看似不确定的方式被唤醒)。为什么会这样?
【问题讨论】:
-
我认为不能保证任何“循环”意义上的调度都是“公平”的。另请注意,您使用获取的信号量调用
sleep(1),这将阻止所有其他调用sem_wait的线程。 -
"将阻止所有其他调用
sem_wait的线程" - 这是故意的。 -
好的,那么我认为它又回到了“公平”问题上。发布信号量后,您的
while循环立即调用sem_wait。不能保证这两个事件之间会发生上下文切换。 -
@DanielWalker 大多数操作系统会在持续获取资源的线程用完其时间片时切换上下文。时间片大小是为该确切目的而选择的。如果您从不让该线程用完它的时间片,那么该线程可能会继续获取资源。但是,您可以不编写执行您不想完成的工作的线程。
-
@DanielWalker 你说上下文永远不会切换似乎很奇怪。但为什么要这样做?不断获取信号量的线程总是可以向前推进并且永远不会用完它的时间片。什么会阻止它?
标签: c multithreading pthreads semaphore