Linux/POSIX中对应Win32的CreateEvent、SetEvent和WaitForSingleObject函数

6

我写了一个小类,用于同步Linux(实际上是Android)和Windows的线程。

这是我的接口的Win32实现:

    class SyncObjectWin32 : public SyncObject
    {
    private:

        const HANDLE m_hEvent;

    public:

        SyncObjectWin32()
          : m_hEvent( ::CreateEvent( NULL, FALSE, FALSE ) )
        {
            if( NULL == m_hEvent )
                throw core::Exception( "sys::SyncObjectWin32::SyncObjectWin32() - Failed to create event." );
        }

        ~SyncObjectWin32()
        {
            ::CloseHandle( m_hEvent );
        }

        void WaitForSignal()
        {
            ::WaitForSingleObject( m_hEvent );
        }

        void Signal()
        {
            ::SetEvent( m_hEvent );
        }
    };

问题在于我不确定POSIX等效的内容是什么。 到目前为止,我已经根据这个SO问题编写了以下类,但由于答案不完整,我不确定如何完成我的类:
    class SyncObjectPosix
    {
    private:

        pthread_mutex_t m_oMutex;

    public:

        SyncObjectPosix()
        {
            pthread_mutex_lock( m_oMutex );                 // lock mutex
            bool & signalled = find_signal( condition );  // find predicate
            signalled = true;                          // set predicate
            pthread_mutex_unlock( m_oMutex );               // unlock mutex
            pthread_cond_signal( condition );            // signal condition variable
        }

        ~SyncObjectPosix()
        {

        }

        void WaitForSignal()
        {
            pthread_mutex_lock(mutex);                         // lock mutex
            bool & signalled = find_signal( condition );          // find predicate
            while (!signalled)
            {
              pthread_cond_timedwait(condition, m_oMutex, timeout);
            }
            signalled = false;                                 // reset predicate
            pthread_mutex_unlock( m_oMutex );                       // unlock mutex
        }

        void Signal()
        {

        }
    };

你在构造函数中的代码看起来应该放在 Signal() 中。 - caf
好的,谢谢。你知道“condition”的类型是什么吗?此外,我怎样才能进行无限等待?还有这个方法有多快?因为如果pthread_cond_timedwait等待1秒钟,那么在某些情况下会出现1秒钟的延迟。 - Virus721
1
Windows和POSIX同步方法的语义不同,因此跨平台等效取决于您如何使用其中之一。从您使用SetEvent()WaitForSingleObject()的方式来看,信号量也可以工作。请参见http://man7.org/linux/man-pages/man3/sem_post.3.html和http://man7.org/linux/man-pages/man3/sem_wait.3.html。在`sem_wait()`手册页上有一些很好的示例代码。 - Andrew Henle
@AndrewHenle 感谢你的帮助。我会看一下。我也找到了这里有关POSIX线程的详细文档(用法语):http://www.univ-orleans.fr/lifo/Members/Sylvain.Jubertie/enseignement/systeme/Processus-Threads.pdf 如果这份文档不能给我所需的,我会研究一下信号量方面的东西。 - Virus721
我看到了这个SO问题:https://dev59.com/YWvXa4cB1Zd3GeqPKpdk 但我不明白为什么需要测试“共享状态”,而不是简单地等待条件被触发。在一个线程上调用pthread_cond_wait(),在另一个线程上调用pthread_cond_signal()不可以吗?谢谢。 - Virus721
显示剩余6条评论
5个回答

18

您所描述的内容在POSIX环境下的等价物是POSIX条件变量。请注意,条件变量必须始终与POSIX互斥锁配对使用,但很多情况下多个条件变量会使用相同的互斥锁,因此如果您不仅仅将互斥锁用于条件变量,就不应该将其置于类中。在Win32和POSIX API之间按含义映射的情况应为:

CreateEvent -> pthread_cond_init

CloseHandle -> pthread_cond_destroy

WaitForSingleObject -> pthread_cond_waitpthread_cond_timedwait

SetEvent -> pthread_cond_signalpthread_cond_broadcast

幸运的是,有很多关于此方面的文档,但我推荐基础的《Programming POSIX Threads》


谢谢你的回答。我会看一下这个。 - Virus721
1
不,Posix条件变量并不能完全替代信号。这真是遗憾。 - SergeyA
@SergeyA 我并不是说它们是相等的。但条件变量适合这种情况。 - tonso
1
pthread(和C++11)的条件变量与 Windows 信号非常不同,特别是手动重置事件,它们无需互斥量即可工作。 - lornova

15

谢谢你的帮助。我猜这是关于Linux的,而不是POSIX对吧?只要它们在Android NDK中得到支持,我也可以使用Linux API。 - Virus721
1
是的,这是针对Linux特定的,我不知道它是否被Android支持,但有可能会被支持。 - j123b567
这是一篇关于eventfdpthread_cond系统性能的有趣阅读材料:https://issues.apache.org/jira/browse/TS-2137。看起来,`eventfd`的速度比`pthread_cond`快5倍。 - j123b567
这是正确的答案,唯一的问题是它不是Posix。但是Posix根本没有信号。 - SergeyA
@j123b567,这里没有任何意外。 - SergeyA
这应该是正确的答案,至少对于那些使用Linux的人来说。谢谢! - Nautilus

7

您的代码的 pthreads 等效版本如下:

class SyncObjectPosix
{
private:

    bool signalled;
    pthread_mutex_t mutex;
    pthread_cond_t cond;

public:

    SyncObjectPosix()
    {
        signalled = false;
        pthread_mutex_init(&mutex, NULL);
        pthread_cond_init(&cond, NULL);
    }

    ~SyncObjectPosix()
    {
        pthread_mutex_destroy(&mutex);
        pthread_cond_destroy(&cond);
    }

    void WaitForSignal()
    {
        pthread_mutex_lock(&mutex);
        while (!signalled)
        {
            pthread_cond_wait(&cond, &mutex);
        }
        signalled = false;
        pthread_mutex_unlock(&mutex);
    }

    void Signal()
    {
        pthread_mutex_lock(&mutex);
        signalled = true;
        pthread_mutex_unlock(&mutex);
        pthread_cond_signal(&cond);
    }
};

2
我们的开源 pevents 库适用于所有平台,是一个实现了这一点的库。它是一小段(单文件)C++ 代码,您可以将其添加到现有项目中,并获得在 pthreads 同步原语上构建的 Windows 事件 API 访问权限。
最重要的提示是它包括 WaitForMultipleObjects 支持。 https://github.com/neosmart/pevents

这是一个非常简单的解决方案,而且效果很好! - BenHero

2
由于Win32的CreateEvent使用类似文件路径的键来标识事件,我不认为所有这些答案都是正确的。
请查看UNIX:System V IPC
int project_id = 1;
std::string path = "[path]";

//CreateEvent
key_t ipc_key = ftok(path.c_str(),project_id);
int event_id = msgget(ipc_key , IPC_CREAT);

//OpenEvent
key_t ipc_key = ftok(path.c_str(),project_id);
int event_id = msgget(ipc_key , 0);

//SetEvent
std::string send_message = "trump 2024|America first|life equally matter|build the #$% wall"; //

msgsnd(event_id, send_message.c_str(), send_message.size(), IPC_NOWAIT); //NO Block
msgsnd(event_id, send_message.c_str(), send_message.size(), 0); //Block

//WaitForSingleObject
std::string receive_message;
receive_message.resize(128);

msgrcv(event_id , &receive_message[0], receive_message.size(), 0,0);

//CloseHandle
msgctl(event_id , IPC_RMID,NULL);

请注意,即使某些功能在每次调用时速度更快,但基于文件描述的功能不会像CreateEvent一样与进程间通信配合。
使用IPC事件消息队列可以解决大部分请求,但它不提供超时能力,这可能会在有些情况下导致一些问题,当您不想完全冻结您的进程/线程时。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接