2009-11-04 49 views
22

我试图将一些Windows代码移植到Linux,理想情况下通过独立于平台的库(例如boost),但是我不确定如何移植此位事件代码。跨平台等效于Windows事件

代码位包含两个线程(让我们称它们为A和B)。 A想要做一些只有B才能做的事情,所以它会发送一条消息,然后等待B完成它。在这个窗口看起来像:

void foo();//thread a calls this 
void bar(HANDLE evt); 

void foo() 
{ 
    HANDLE evt = CreateEvent(0,FALSE,FALSE,0); 
    bCall(boost::bind(&bar, evt)); 
    WaitForSingleObject(evt,INFINITE); 
    CloseHandle(evt); 
} 
void bar(HANDLE evt) 
{ 
    doSomething(); 
    SetEvent(evt); 
} 

我看着升压::线程库,但它没有似乎有什么,这样做,我能看到的是关闭了boost :: condition_variable,但它似乎这意味着与一个互斥体结合使用,在这里不是这种情况。

+0

我认为你的特定于Windows的代码在引擎盖下使用了一个互斥锁。它只是把这个摘要摘下来。 – rmeador 2009-11-04 23:26:57

+0

[pthread-like windows手动重置事件]的可能重复(http://stackoverflow.com/questions/178114/pthread-like-windows-manual-reset-event) – jww 2014-02-11 13:36:29

+0

[this question](http:// stackoverflow .com/questions/4692717/win32-reset-event-like-synchronization-class-with-boost-c)也有很好的信息 – Loomchild 2017-01-17 10:44:27

回答

10

我认为一个良好的,跨平台相当于win32的事件是boost::condition,所以你的代码可能是这个样子:

void foo() 
{ 
    boost::mutex mtxWait; 
    boost::condition cndSignal; 

    bCall(boost::bind(&bar, mtxWait, cndSignal)); 

    boost::mutex::scoped_lock mtxWaitLock(mtxWait); 
    cndSignal.wait(mtxWait); // you could also use cndSignal.timed_wait() here 
} 

void bar(boost::mutex& mtxWait, boost::condition& cndSignal) 
{ 
    doSomething(); 
    cndSignal.notify_one(); 
} 
+1

虽然这个互斥体真的需要吗?我当然想知道这个事实,它意味着B可能会阻塞在酒吧(如果线程A的时间片在mtxWaitLock和cndSignal.wait之间过期)。 – 2009-11-04 22:47:06

+0

不需要第二个互斥锁。你不需要持有互斥体来调用notify(有些情况下你需要)。 – 2009-11-04 23:25:43

+0

是的你说得对 - 我会修改代码 – Alan 2009-11-04 23:54:34

2

可以使用升压线程barrier

#include <boost/thread/thread.hpp> 
#include <boost/thread/barrier.hpp> 
#include <iostream> 

void foo(boost::barrier* b) 
{ 
    std::cout << "foo done" << std::endl; 
    b->wait(); 
} 


int main() 
{ 
    std::cout << "start foo" << std::endl; 
    boost::barrier b(2); 

    boost::thread t(&foo, &b); 
    b.wait(); 
    std::cout << "after foo done" << std::endl; 
    t.join(); 
} 
+1

在某些情况下工作,但是障碍缺少一个超时值地方。 – 2009-11-04 22:41:11

-1

在POSIX兼容的系统,您可以使用POSIX IPC。它用于进程间/线程间消息传递。如果我没有记错,可以使用cygwin端口。

0

我做(或看到)都在不同时期下这样的事情的:

使用互斥+条件变量。

使用一个管道,使foo创建管道并将其写入结束条。当酒吧完成后,酒吧写入管道。 (这甚至可以运行多进程)。

有一个布尔值(是的,这是一个坏主意。)

0

它看起来像你正在寻找信号插槽mechanizm FOO调查。你可以找到一个在:

boostQt

两个跨平台。

Qt的例子:

#include <QObject> 

class Counter : public QObject 
{ 
    Q_OBJECT 
public: 
    Counter() { m_value = 0; } 

    int value() const { return m_value; } 

public slots: 
    void setValue(int value); 

signals: 
    void valueChanged(int newValue); 

private: 
    int m_value; 
}; 

Counter a, b; 
QObject::connect(&a, SIGNAL(valueChanged(int)), 
        &b, SLOT(setValue(int))); 

a.setValue(12);  // a.value() == 12, b.value() == 12 
b.setValue(48);  // a.value() == 12, b.value() == 48 

void Counter::setValue(int value) 
{ 
    if (value != m_value) { 
     m_value = value; 
     emit valueChanged(value); 
    } 
} 
+1

我不认为升压信号/插槽机制有任何类型的等待机制 – 2009-11-04 22:47:31

+0

但是对于信号/插槽,我没有看到有什么理由等待()? 如果准备好了,只需发布​​消息,任何感兴趣的客户都会采取适当的行动。 简单的观察者设计模式。 – bua 2009-11-04 23:01:44

+0

@bua执行'waitForReadyRead'方法至少有一个等待的理由。在这种情况下,您必须等待数据,并且不应该阻止QT事件。 – baltazar 2012-01-13 13:41:31

14

所有这些问题的答案都太复杂的,来吧人们这并不难。

namespace porting 
{ 
    class Event; 
    typedef Event* Event_handle; 
    static const unsigned k_INFINITE = 0xFFFFFFFF; 

    class Event 
    { 
     friend Event_handle CreateEvent(void); 
     friend void CloseHandle(Event_handle evt); 
     friend void SetEvent(Event_handle evt); 
     friend void WaitForSingleObject(Event_handle evt, unsigned timeout); 

     Event(void) : m_bool(false) { } 

     bool m_bool; 
     boost::mutex m_mutex; 
     boost::condition m_condition; 
    }; 

    Event_handle CreateEvent(void) 
    { return new Event; } 

    void CloseHandle(Event_handle evt) 
    { delete evt; } 

    void SetEvent(Event_handle evt) 
    { 
     evt->m_bool = true; 
     evt->m_cond.notify_all(); 
    } 

    void WaitForSingleObject(Event_handle evt, unsigned timeout) 
    { 
     boost::scoped_lock lock(evt->m_mutex); 
     if(timeout == k_INFINITE) 
     { 
     while(!evt->m_bool) 
     { 
      evt->m_cond.wait(lock); 
     } 
     } 
     else 
     { 
     //slightly more complex code for timeouts 
     } 
    } 

}// porting 

void foo() 
{ 
    porting::Event_handle evt = porting::CreateEvent(); 
    bCall(boost::bind(&bar, evt)); 
    porting::WaitForSingleObject(evt, porting::k_INFINITE); 
    porting::CloseHandle(evt); 
} 

void bar(porting::Event_handle evt) 
{ 
    doSomething(); 
    porting::SetEvent(evt); 
} 

有可能是多一点做的就是这个工作完全因为我不熟悉的WaitForSingleObject语义(如果两个线程调用它同时会发生什么,会发生什么,如果在同一个线程称它两次)。但是,解决方案看起来非常像这样。

0

从Boost.Thread 1.47版本documentation

的类condition_variable和condition_variable_any提供 机制,一个线程等待通知从另一个线程 一个特定的条件已经成为现实。

4

由于评论已关闭,我不得不发表我的评论到以前的帖子作为答案。但实际上我没有回答。

1)有与@Alan的解决的问题。 他提供的示例代码运行良好。但它与Windows事件功能不同。当Windows Event对象是任意数量的后续调用WaitForSingleObject立即返回,显示该对象处于信号状态。但有了boost的mutex/condition解决方案,bar()必须通知需要它的每个foo()调用的条件。这使得“跨平台”Windows事件功能的情况变得更加困难。 notify_all()也帮不上忙。

当然这是在@ deft_code的示例代码通过使用布尔变量以某种方式解决的。 (虽然它受到本身的竞争条件的问题。试想,如果SetEvent(...)while(!evt->m_bool)evt->m_cond.wait(lock)从一个单独的线程中调用之前死了。会发生死锁。这可以然而,可以通过使用一些竞争条件的管理技术,使两个报表解决while()wait()原子),但它有自己的缺点:

2)还有在利用升压与@deft_code的代码问题mutex/condition/bool组合:在Windows

事件对象可以是,命名为它使它们能够用于进程间同步。例如,过程A可以创建一个命名事件并将其设置为:SetEvent(hFileIsReady)。随后,被设置任何等待该事件处理号(从而调用WaitForSingleObject(hFileIsReady))将立即继续正常执行,直到事件再次进程A内ResetEvent(hFileIsReady)复位。

但是组合mutex/condition/bool无法承担这样的功能。当然,我们可以使用boost named_conditionnamed_mutex。但是,等待之前我们必须检查的布尔变量呢?

8

你可以使用一个承诺和未来,从升压螺纹:

#include <boost\thread.hpp> 

boost::promise<bool> prom; 

void foo() 
{ 
    auto future = prom.get_future(); 
    auto result = future.wait_for(boost::chrono::milliseconds(1000)); 
    // we get here if (a) 1 second passes or (b) bar sets the promise value 
    if (result==boost::future_status::ready) 
    { 
     /* bar set the promise value */ 
    } 
    if (result==boost::future_status::timeout) 
    { 
     /* 1 second passed without bar setting promise value */ 
    } 
} 

void bar() 
{ 
    prom.set_value(true); 
} 
2

对于任何参与或移植工作的多线程的原生Windows的C/C++代码的Linux/Mac上,we've authored an open source (MIT-licensed) library同时实现手动和自动重置pthread上的WIN32事件,包括完整的实现WaitForSingleObjectWaitForMultipleObjects,使它成为我所知道的Linux/Mac上唯一的WFMO端口。

pevents is available on GitHub并已相当久经考验的,是一些大牌的使用;还有一个漂浮在某个地方的大型活动。

使用pevents将使Windows移植代码更容易,因为底层范例在Windows和posix平台之间显着不同 - 尽管我鼓励任何编写多平台代码的人使用现有的跨平台多线程库,如boost第一个地方。

+0

谢谢!我一直在寻找适合年龄的便携式WFMO解决方案。很遗憾C++标准没有提供任何接近它的东西,AFAIK,boost也不是。我绝对有一些立即使用你的图书馆。 – 2017-09-29 14:45:51