【发布时间】:2011-12-19 22:11:15
【问题描述】:
我遇到的所有协程实现都使用汇编或检查jmp_buf 的内容。问题在于它本质上不是跨平台的。
我认为以下实现不会陷入未定义的行为或依赖于实现细节。但是我从来没有遇到过这样写的协程。
在线程中使用长跳是否存在一些固有缺陷?
这段代码中是否存在一些隐藏的问题?
#include <setjmp.h>
#include <thread>
class Coroutine
{
public:
Coroutine( void ) :
m_done( false ),
m_thread( [&](){ this->start(); } )
{ }
~Coroutine( void )
{
std::lock_guard<std::mutex> lock( m_mutex );
m_done = true;
m_condition.notify_one();
m_thread.join();
}
void start( void )
{
if( setjmp( m_resume ) == 0 )
{
std::unique_lock<std::mutex> lock( m_mutex );
m_condition.wait( lock, [&](){ return m_done; } );
}
else
{
routine();
longjmp( m_yield, 1 );
}
}
void resume( void )
{
if( setjmp( m_yield ) == 0 )
{
longjmp( m_resume, 1 );
}
}
void yield( void )
{
if( setjmp( m_resume ) == 0 )
{
longjmp( m_yield, 1 );
}
}
private:
virtual void routine( void ) = 0;
jmp_buf m_resume;
jmp_buf m_yield;
bool m_done;
std::mutex m_mutex;
std::condition_variable m_condition;
std::thread m_thread;
};
【问题讨论】:
-
协程在上个世纪很流行。当多核处理器变得普遍时,彻底过时了。除非这是出于学术兴趣,否则请务必利用线程并避免 setjmp() 的恐怖。
-
为了并发,我对协程不感兴趣。它们有许多有用的特性,而穷人的并发不是其中之一。 Lua example 和 wikipedia reference
-
@Hans Passant -- 协程绝对不会消失,不管处理器有多少内核,因为上下文切换要快得多,你可以有两个订单协同例程比线程多,执行顺序有时很重要。
-
@Hans Passant -- 我认为我解释得不好。纤程(协程)不会引起内核上下文切换,它们在单个线程中运行,上下文切换就像一个跳远。我的第二点是因为它们在单个线程中运行,它们是非抢占式的。不仅没有锁定、竞态等,还保证了纤程的执行顺序。它们是偶数驱动模拟器中的基本原语,其中事件的排序是必不可少的。它们不能被抢占式线程替代。
-
@HansPassant:我认为并发和并行之间存在混淆。如果您看一下“新”语言,例如 Go 或 Haskell,您会注意到它们已针对并发性进行了定制,并提供了“轻量级”执行线程。它们不会从本质上增加应用程序的并行度(无论如何,您可以获得的最大并行度是受硬件限制的),但确实允许您定义数千个同时发展的轻量级任务。恕我直言,协程用于并发,可能适用于并行性,但不一定。
标签: c++ cross-platform c++11 coroutine