我应该如何用计数模拟 sem_wait?
How should I simulate sem_wait with a count?
我正在使用 semaphore.h
,如果 n
而不是只有一个插槽可用,我想获得一个信号量。 Posix 本机不提供此功能。我该如何解决这个问题?我必须使用信号量,没有其他同步方式是可能的。
我正在考虑使用带有单独计数器变量的二进制信号量,但在我看来,这有点违背了它的目的。
由于您有多个线程争用信号量的插槽(否则您根本不需要信号量),因此您需要防止死锁。例如,如果您的信号量有四个插槽,并且两个线程中的每一个都试图获取三个,那么如果每个线程都设法获取两个,它们就会死锁。由此可见,必须对获取信号量槽的进程进行访问保护。
保护计数器的二进制信号量不足以防止上述死锁情况。此外,如果在任何给定时间都没有足够的插槽可用,那么您必须有一些同步方法来等待更多插槽可用。不过,您可以使用两个信号量来完成这项工作,一个用于保护对信号量获取过程的访问,另一个用于承载正在获取的实际槽。像这样的东西,例如:
#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
的插槽,而它已经拥有至少一个插槽(以及其他方式),那么它仍然容易出现死锁。我认为风险是问题中固有的。
我正在使用 semaphore.h
,如果 n
而不是只有一个插槽可用,我想获得一个信号量。 Posix 本机不提供此功能。我该如何解决这个问题?我必须使用信号量,没有其他同步方式是可能的。
我正在考虑使用带有单独计数器变量的二进制信号量,但在我看来,这有点违背了它的目的。
由于您有多个线程争用信号量的插槽(否则您根本不需要信号量),因此您需要防止死锁。例如,如果您的信号量有四个插槽,并且两个线程中的每一个都试图获取三个,那么如果每个线程都设法获取两个,它们就会死锁。由此可见,必须对获取信号量槽的进程进行访问保护。
保护计数器的二进制信号量不足以防止上述死锁情况。此外,如果在任何给定时间都没有足够的插槽可用,那么您必须有一些同步方法来等待更多插槽可用。不过,您可以使用两个信号量来完成这项工作,一个用于保护对信号量获取过程的访问,另一个用于承载正在获取的实际槽。像这样的东西,例如:
#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
的插槽,而它已经拥有至少一个插槽(以及其他方式),那么它仍然容易出现死锁。我认为风险是问题中固有的。