我正在尝试将使用Semaphores的项目(从linux)移植到Mac OS X,但是某些posix信号量并未在Mac OS X上实现
我在此端口中遇到的是sem_timedwait()
我对信号量知之甚少,但是从手册页sem_wait()
似乎接近sem_timedwait
并且已经实现了
从手册页
sem_timedwait()
功能必须 锁定由
引用的信号量 与sem
函数中的sem_wait()
一样。 但是,如果信号量不能为
锁定而不等待另一个 进程或线程解锁 通过执行sem_post()
信号量 功能,这个等待应该是 指定的超时时间 到期
根据我对semphores如何工作的有限理解,我可以看到sem_timedwait()
更安全,但我仍然可以使用sem_wait()
这是对的吗?如果不是我还有其他选择......
由于
答案 0 :(得分:6)
超时很可能对算法的运行很重要。因此,仅使用sem_wait()
可能无效。
你可以使用sem_trywait()
,它会在所有情况下立即返回。然后,您可以循环,并使用您选择的休眠间隔,每次减少总超时,直到超时或获取信号量为止。
更好的解决方案是重写算法以使用条件变量,然后您可以使用pthread_cond_timedwait()
来获得适当的超时。
答案 1 :(得分:3)
您是否考虑过使用apache可移植运行时?它预装在每个Mac OS X Box和许多Linux发行版上,它带有一个围绕线程并发的平台中立包装,甚至可以在MS Windows上运行:
http://apr.apache.org/docs/apr/1.3/group__apr__thread__cond.html
答案 2 :(得分:2)
另一种选择可能是使用sem_timedwait.c 由澳大利亚天文台软件组的Keith Shortridge实施。
来自源文件:
/*
* s e m _ t i m e d w a i t
*
* Function:
* Implements a version of sem_timedwait().
*
* Description:
* Not all systems implement sem_timedwait(), which is a version of
* sem_wait() with a timeout. Mac OS X is one example, at least up to
* and including version 10.6 (Leopard). If such a function is needed,
* this code provides a reasonable implementation, which I think is
* compatible with the standard version, although possibly less
* efficient. It works by creating a thread that interrupts a normal
* sem_wait() call after the specified timeout.
*
* ...
*
* Limitations:
*
* The mechanism used involves sending a SIGUSR2 signal to the thread
* calling sem_timedwait(). The handler for this signal is set to a null
* routine which does nothing, and with any flags for the signal
* (eg SA_RESTART) cleared. Note that this effective disabling of the
* SIGUSR2 signal is a side-effect of using this routine, and means it
* may not be a completely transparent plug-in replacement for a
* 'normal' sig_timedwait() call. Since OS X does not declare the
* sem_timedwait() call in its standard include files, the relevant
* declaration (shown above in the man pages extract) will probably have
* to be added to any code that uses this.
*
* ...
*
* Copyright (c) Australian Astronomical Observatory.
* Commercial use requires permission.
* This code comes with absolutely no warranty of any kind.
*/
答案 3 :(得分:1)
你是否可以尝试模拟sem_timedwait()调用的功能,方法是在计时器到期后调用sem_post()的另一个线程中启动一个计时器,如果它没有被应该调用sem_post的主线程调用( )?
答案 4 :(得分:1)
我认为最简单的解决方案是将sem_wait()与对alarm()的调用结合使用以唤醒中止等待。例如:
alarm(2);
int return_value = sem_wait( &your_semaphore );
if( return_value == EINTR )
printf( "we have been interrupted by the alarm." );
一个问题是警报需要几秒钟作为输入,因此在您的情况下,定时等待时间可能太长。
- aghiles
答案 5 :(得分:1)
我以前在OSX上使用命名信号量,但现在sem_timedwait不可用,并且不推荐使用sem_init和friends。我使用pthread mutex实现了信号量,条件如下,对我有用(OSX 10.13.1)。您可能必须创建一个句柄vs结构表,如果它不能在其中保存ptr,则查找sem_t类型(即指针是64位,sem_t是32?)
#ifdef __APPLE__
typedef struct
{
pthread_mutex_t count_lock;
pthread_cond_t count_bump;
unsigned count;
}
bosal_sem_t;
int sem_init(sem_t *psem, int flags, unsigned count)
{
bosal_sem_t *pnewsem;
int result;
pnewsem = (bosal_sem_t *)malloc(sizeof(bosal_sem_t));
if (! pnewsem)
{
return -1;
}
result = pthread_mutex_init(&pnewsem->count_lock, NULL);
if (result)
{
free(pnewsem);
return result;
}
result = pthread_cond_init(&pnewsem->count_bump, NULL);
if (result)
{
pthread_mutex_destroy(&pnewsem->count_lock);
free(pnewsem);
return result;
}
pnewsem->count = count;
*psem = (sem_t)pnewsem;
return 0;
}
int sem_destroy(sem_t *psem)
{
bosal_sem_t *poldsem;
if (! psem)
{
return EINVAL;
}
poldsem = (bosal_sem_t *)*psem;
pthread_mutex_destroy(&poldsem->count_lock);
pthread_cond_destroy(&poldsem->count_bump);
free(poldsem);
return 0;
}
int sem_post(sem_t *psem)
{
bosal_sem_t *pxsem;
int result, xresult;
if (! psem)
{
return EINVAL;
}
pxsem = (bosal_sem_t *)*psem;
result = pthread_mutex_lock(&pxsem->count_lock);
if (result)
{
return result;
}
pxsem->count = pxsem->count + 1;
xresult = pthread_cond_signal(&pxsem->count_bump);
result = pthread_mutex_unlock(&pxsem->count_lock);
if (result)
{
return result;
}
if (xresult)
{
errno = xresult;
return -1;
}
}
int sem_trywait(sem_t *psem)
{
bosal_sem_t *pxsem;
int result, xresult;
if (! psem)
{
return EINVAL;
}
pxsem = (bosal_sem_t *)*psem;
result = pthread_mutex_lock(&pxsem->count_lock);
if (result)
{
return result;
}
xresult = 0;
if (pxsem->count > 0)
{
pxsem->count--;
}
else
{
xresult = EAGAIN;
}
result = pthread_mutex_unlock(&pxsem->count_lock);
if (result)
{
return result;
}
if (xresult)
{
errno = xresult;
return -1;
}
return 0;
}
int sem_wait(sem_t *psem)
{
bosal_sem_t *pxsem;
int result, xresult;
if (! psem)
{
return EINVAL;
}
pxsem = (bosal_sem_t *)*psem;
result = pthread_mutex_lock(&pxsem->count_lock);
if (result)
{
return result;
}
xresult = 0;
if (pxsem->count == 0)
{
xresult = pthread_cond_wait(&pxsem->count_bump, &pxsem->count_lock);
}
if (! xresult)
{
if (pxsem->count > 0)
{
pxsem->count--;
}
}
result = pthread_mutex_unlock(&pxsem->count_lock);
if (result)
{
return result;
}
if (xresult)
{
errno = xresult;
return -1;
}
return 0;
}
int sem_timedwait(sem_t *psem, const struct timespec *abstim)
{
bosal_sem_t *pxsem;
int result, xresult;
if (! psem)
{
return EINVAL;
}
pxsem = (bosal_sem_t *)*psem;
result = pthread_mutex_lock(&pxsem->count_lock);
if (result)
{
return result;
}
xresult = 0;
if (pxsem->count == 0)
{
xresult = pthread_cond_timedwait(&pxsem->count_bump, &pxsem->count_lock, abstim);
}
if (! xresult)
{
if (pxsem->count > 0)
{
pxsem->count--;
}
}
result = pthread_mutex_unlock(&pxsem->count_lock);
if (result)
{
return result;
}
if (xresult)
{
errno = xresult;
return -1;
}
return 0;
}
#endif
答案 6 :(得分:0)
如果您可以使用MP API:
MPWaitOnSemaphore
与kMPTimeoutErr
一起存在,如果超过指定的超时而没有发出信号。
答案 7 :(得分:0)
我计划使用以下函数作为替换,但后来我发现sem_getvalue()在OSX上也已弃用且无法正常运行。您可以在MIT或LGPL许可证(您的选择)下使用以下稍微未经测试的代码。
#ifdef __APPLE__
struct CSGX__sem_timedwait_Info
{
pthread_mutex_t MxMutex;
pthread_cond_t MxCondition;
pthread_t MxParent;
struct timespec MxTimeout;
bool MxSignaled;
};
void *CSGX__sem_timedwait_Child(void *MainPtr)
{
CSGX__sem_timedwait_Info *TempInfo = (CSGX__sem_timedwait_Info *)MainPtr;
pthread_mutex_lock(&TempInfo->MxMutex);
// Wait until the timeout or the condition is signaled, whichever comes first.
int Result;
do
{
Result = pthread_cond_timedwait(&TempInfo->MxCondition, &TempInfo->MxMutex, &TempInfo->MxTimeout);
if (!Result) break;
} while (1);
if (errno == ETIMEDOUT && !TempInfo->MxSignaled)
{
TempInfo->MxSignaled = true;
pthread_kill(TempInfo->MxParent, SIGALRM);
}
pthread_mutex_unlock(&TempInfo->MxMutex);
return NULL;
}
int sem_timedwait(sem_t *sem, const struct timespec *abs_timeout)
{
// Quick test to see if a lock can be immediately obtained.
int Result;
do
{
Result = sem_trywait(sem);
if (!Result) return 0;
} while (Result < 0 && errno == EINTR);
// Since it couldn't be obtained immediately, it is time to shuttle the request off to a thread.
// Depending on the timeout, this could take longer than the timeout.
CSGX__sem_timedwait_Info TempInfo;
pthread_mutex_init(&TempInfo.MxMutex, NULL);
pthread_cond_init(&TempInfo.MxCondition, NULL);
TempInfo.MxParent = pthread_self();
TempInfo.MxTimeout.tv_sec = abs_timeout->tv_sec;
TempInfo.MxTimeout.tv_nsec = abs_timeout->tv_nsec;
TempInfo.MxSignaled = false;
sighandler_t OldSigHandler = signal(SIGALRM, SIG_DFL);
pthread_t ChildThread;
pthread_create(&ChildThread, NULL, CSGX__sem_timedwait_Child, &TempInfo);
// Wait for the semaphore, the timeout to expire, or an unexpected error condition.
do
{
Result = sem_wait(sem);
if (Result == 0 || TempInfo.MxSignaled || (Result < 0 && errno != EINTR)) break;
} while (1);
// Terminate the thread (if it is still running).
TempInfo.MxSignaled = true;
int LastError = errno;
pthread_mutex_lock(&TempInfo.MxMutex);
pthread_cond_signal(&TempInfo.MxCondition);
pthread_mutex_unlock(&TempInfo.MxMutex);
pthread_join(ChildThread, NULL);
pthread_cond_destroy(&TempInfo.MxCondition);
pthread_mutex_destroy(&TempInfo.MxMutex);
// Restore previous signal handler.
signal(SIGALRM, OldSigHandler);
errno = LastError;
return Result;
}
#endif
SIGALRM比SIGUSR2更有意义,因为这里显然使用了另一个例子(我没有看过它)。 SIGALRM主要用于alarm()调用,当你想要亚秒级分辨率时几乎没用。
此代码首先尝试使用sem_trywait()获取信号量。如果那立即成功,那么它就会挽救。否则,它启动一个线程,该线程通过pthread_cond_timedwait()实现定时器。 MxSignaled布尔值用于确定超时状态。
您可能还会发现此相关函数对调用上述sem_timedwait()实现很有用(再次,MIT或LGPL,您的选择):
int CSGX__ClockGetTimeRealtime(struct timespec *ts)
{
#ifdef __APPLE__
clock_serv_t cclock;
mach_timespec_t mts;
if (host_get_clock_service(mach_host_self(), CALENDAR_CLOCK, &cclock) != KERN_SUCCESS) return -1;
if (clock_get_time(cclock, &mts) != KERN_SUCCESS) return -1;
if (mach_port_deallocate(mach_task_self(), cclock) != KERN_SUCCESS) return -1;
ts->tv_sec = mts.tv_sec;
ts->tv_nsec = mts.tv_nsec;
return 0;
#else
return clock_gettime(CLOCK_REALTIME, ts);
#endif
}
帮助填充timepec结构,其中最接近的是clock_gettime()可以提供的结构。有各种各样的评论,反复调用host_get_clock_service()很昂贵。但是启动一个线程也很昂贵。
真正的解决方案是让Apple实施整个POSIX规范,而不仅仅是强制性部分。仅实现POSIX的强制性位,然后声称POSIX合规性只会让每个人都有一个半破坏的操作系统和大量的解决方案,如上所述可能具有不太理想的性能。
以上都说过,我放弃了Mac OSX和Linux上的原生信号量(Sys V和POSIX)。它们以相当不幸的方式被打破。其他人也应该放弃他们。 (我不会放弃那些操作系统上的信号量,只是原生实现。)无论如何,现在每个人都有一个没有商业限制的sem_timedwait()实现,其他人可以将意大利面复制到他们心中的内容。
答案 8 :(得分:0)
一种选择是使用低级信号量马赫API:
#include <mach/semaphore.h>
semaphore_create(...)
semaphore_wait(...)
semaphore_timedwait(...)
semaphore_signal(...)
semaphore_destroy(...)
它在libuv BTW中使用。
参考: