我该如何用计数模拟sem_wait?

时间:2015-06-06 12:10:50

标签: c posix semaphore

我正在使用semaphore.h并希望获得信号量,如果n而不是只有一个广告位可用。 Posix本身不提供此功能。我该如何解决这个问题?我一定会使用信号量,没有其他的同步手段。

我正在考虑使用带有单独计数器变量的二进制信号量,但在我看来,这会破坏它的目的。

1 个答案:

答案 0 :(得分:1)

由于您有多个线程争用信号量的插槽(否则您根本不需要信号量),您需要防止死锁。例如,如果您的信号量有四个插槽,并且两个线程中的每一个都试图获取三个,那么如果每个线程都设法获得两个,则它们将会死锁。因此,您必须保护对获取信号量槽的过程的访问权。

保护计数器的二进制信号量不足以防止上述死锁情况。此外,如果在任何给定时间没有足够的插槽可用,那么您必须有一些同步方法来等待更多插槽可用。但是,您可以使用两个信号量来完成工作,一个用于保护对信号量获取过程的访问,另一个用于获取正在获取的实际插槽。像这样的东西,例如:

#define DO_OR_RETURN(x) do { int _r; if ((_r = (x))) return _r; } while (0)

typedef struct multi_sem {
    sem_t sem_acquire_sem;
    sem_t multislot_sem;
} multisem;

int multisem_init(multisem *ms, unsigned int slots) {
    DO_OR_RETURN(sem_init(&ms->sem_acquire_sem, 0, 1));
    return sem_init(&ms->multislot_sem, 0, slots);
}

int multisem_wait(multisem *ms, unsigned int slots_to_acquire) {
    int result;
    DO_OR_RETURN(sem_wait(&ms->sem_acquire_sem));

    while (slots_to_acquire) {
        result = sem_wait(&ms->multislot_sem);
        switch (result) {
            case 0:
                slots_to_acquire -= 1;
                break;
            case EINTR:
                /* interrupted by a signal; try again */
                break;
            default:
                /* undocumented error - should never happen */
                /* insert appropriate apocalypse response here */
                slots_to_acquire = 0; /* bail out */
                break;
        }
    }

    if (sem_post(&ms->sem_acquire_sem)) {
        /* big oops - no recovery possible - should never happen */
        /* insert appropriate apocalypse response here */
    }

    return result;
}

int multisem_post(multisem *ms, unsigned int slots_to_post) {
    while (slots_to_post) {
        DO_OR_RETURN(sem_post(&ms->multislot_sem));
        slots_to_post -= 1;
    }
    return 0;
}

请注意,如果线程在已经拥有至少一个(以及其他方式)的情况下尝试获取multisem的槽,则仍然容易发生死锁。我认为风险是问题所固有的。