15

我正在尝试将使用信号量的项目(来自 linux)移植到 Mac OS X,但是某些 posix 信号量未在 Mac OS X 上实现

我在这个港口打的那个是sem_timedwait()

我对信号量了解不多,但从手册页sem_wait()看来似乎很接近sem_timedwait并且已实现

从手册页

sem_timedwait()
sem函数应锁定函数中 as 引用的信号量sem_wait()。但是,如果在
不等待另一个进程或线程
通过执行某个sem_post() 功能来解锁信号量的情况下无法锁定信号量,则应
在指定的超时到期时终止此等待

从我对信号量如何工作的有限理解来看,我认为这sem_timedwait()更安全,但我仍然应该能够使用 sem_wait()

它是否正确?如果不是我还有什么其他选择...

谢谢

4

9 回答 9

7

超时可能对算法的操作很重要。因此,仅使用sem_wait()可能行不通。

您可以使用sem_trywait(), 在所有情况下都会立即返回。然后,您可以循环并使用您选择的睡眠间隔,每次都会减少总超时,直到您用完超时或获取信号量。

一个更好的解决方案是重写算法以使用条件变量,然后您可以使用它pthread_cond_timedwait()来获得适当的超时。

于 2009-05-03T07:03:07.427 回答
3

另一种选择可能是使用 澳大利亚天文台软件组的 Keith Shortridge 的sem_timedwait.c实现。

从源文件:

/*
*                       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.
*/
于 2013-03-01T17:44:19.907 回答
3

我曾经在 OSX 上使用命名信号量,但现在 sem_timedwait 不可用,并且不推荐使用 sem_init 和朋友。我使用 pthread 互斥体和条件实现了信号量,如下对我有用(OSX 10.13.1)。您可能需要制作一个句柄与结构表并查找 sem_t 类型,如果它不能在其中保存一个 ptr(即指针是 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
于 2018-02-14T01:47:47.150 回答
2

您是否考虑过使用 apache 可移植运行时?它预装在每个 Mac OS X Box 和许多 Linux 发行版上,它带有一个围绕线程并发的平台中立包装器,甚至可以在 MS Windows 上工作:

http://apr.apache.org/docs/apr/1.3/group__apr__thread__cond.html

于 2009-05-03T16:15:16.693 回答
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." );

一个问题是警报需要几秒钟作为输入,因此在您的情况下,定时等待可能太长了。

——阿吉利斯

于 2011-12-30T05:54:15.350 回答
1

一种选择是使用低级 semaphore mach API:

#include <mach/semaphore.h>

semaphore_create(...)

semaphore_wait(...)
semaphore_timedwait(...)
semaphore_signal(...)

semaphore_destroy(...)

它用于libuv BTW。

参考:

于 2020-05-15T16:34:14.443 回答
0

如果没有被应该调用 sem_post() 的主线程调用,您能否尝试通过在另一个调用 sem_post() 的线程中启动一个定时器来模拟 sem_timedwait() 调用的功能?

于 2009-05-03T05:58:36.847 回答
0

如果您只能使用 MP API:

  • MPCreateSemaphore/MPDeleteSemaphore
  • MPSignalSemaphore/MPWaitOnSemaphore

MPWaitOnSemaphorekMPTimeoutErr如果超过指定的超时时间而没有发出信号,则存在。

于 2010-03-03T16:08:40.083 回答
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
}

帮助使用与 clock_gettime() 可以提供的最接近的东西填充 timespec 结构。有各种各样的评论说重复调用 host_get_clock_service() 是昂贵的。但是启动线程也很昂贵。

真正的解决方案是让 Apple 实现整个 POSIX 规范,而不仅仅是强制性部分。只实施 POSIX 的强制性部分,然后声称 POSIX 合规性只会让每个人都有一个半损坏的操作系统和大量类似上述的变通方法,这些变通方法的性能可能不太理想。

综上所述,我放弃了 Mac OSX 和 Linux 上的本地信号量(Sys V 和 POSIX)。它们以很多相当不幸的方式被破坏。其他人也应该放弃他们。(我不会放弃那些操作系统上的信号量,只是放弃本机实现。)无论如何,现在每个人都有一个没有商业限制的 sem_timedwait() 实现,其他人可以复制意大利面到他们心中的内容。

于 2016-05-19T13:22:08.147 回答