C 或 C++ 中的协程?

发布于 2024-12-04 15:58:25 字数 323 浏览 2 评论 0原文

看起来协程通常出现在高级语言中。

它们似乎也有几种不同的定义。我正在尝试找到一种方法,在 C 中拥有专门调用的协程,就像我们在 Lua 中一样。

 function foo()     
     print("foo", 1) 
     coroutine.yield() 
     print("foo", 2) 
 end 

It seems like coroutines are normally found in higher level languages.

There seem to be several different definitions of them as well. I am trying to find a way to have the specifically called coroutines in C like we have in Lua.

 function foo()     
     print("foo", 1) 
     coroutine.yield() 
     print("foo", 2) 
 end 

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

分开我的手 2024-12-11 15:58:25

C 或 C++ 中的协程没有语言级别的支持。

您可以使用汇编程序或光纤来实现它们,但结果不可移植,并且在 C++ 的情况下,您几乎肯定会失去使用异常的能力,并且无法依赖堆栈展开进行清理。

在我看来,你应该使用支持它们的语言,或者不使用它们 - 用不支持它们的语言实现你自己的版本是自找麻烦。

There's no language level support for coroutines in either C or C++.

You could implement them using assembler or fibres, but the result would not be portable and in the case of C++ you'd almost certainly lose the ability to use exceptions and be unable to rely on stack unwinding for cleanup.

In my opinion you should either use a language the supports them or not use them - implementing your own version in a language that doesn't support them is asking for trouble.

海之角 2024-12-11 15:58:25

Boost C++ 库中有一个新的(自版本 1.53.0 起)协程库: http://www.boost.org/doc/libs/1_53_0/libs/coroutine/doc/html/index.html

我是不知道 C 库——我在寻找一个库时遇到了这个问题。

There is a new (as of version 1.53.0) coroutine library in the Boost C++ library: http://www.boost.org/doc/libs/1_53_0/libs/coroutine/doc/html/index.html

I'm unaware of a C library--I came across this question looking for one.

蓬勃野心 2024-12-11 15:58:25

如今,C++ 原生提供协程作为 C++20 的一部分。

对于 C 语言,它们本身不支持,但有几个库提供了它们。有些是不可移植的,因为它们依赖于一些与体系结构相关的汇编指令,但有些是可移植的,因为它们使用标准库函数,例如 setjmp()/longjmp()getcontext()/setcontext()/makecontext()/swapcontext()。还有一些原始命题,例如使用 C 的 one来自 Duff 的设备的语言技巧。

注意:就我而言,我设计了这个

Nowadays, C++ provides coroutines natively as part of C++20.

Concerning the C language, they are not supported natively but several libraries provides them. Some are not portable as they rely on some architecture-dependent assembly instructions but some are portable as they use standard library functions like setjmp()/longjmp() or getcontext()/setcontext()/makecontext()/swapcontext(). There are also some original propositions like this one which uses the C language trick from the Duff's device.

N.B.: On my side, I designed this library.

惜醉颜 2024-12-11 15:58:25

抱歉 - C 和 C++ 都不支持协程。然而,简单搜索“C 协程:会产生以下有关该问题的精彩论文:http://www.chiark.greenend.org.uk/~sgtatham/coroutines.html,尽管你可能会发现他的解决方案有点 - 嗯 - 不切实际

Sorry - neither C nor C++ has support for coroutines. However, a simple search for "C coroutine: yields the following fascinating treatise on the problem: http://www.chiark.greenend.org.uk/~sgtatham/coroutines.html, although you may find his solution a bit - um - impractical

别想她 2024-12-11 15:58:25

有很多 C++ 协程库。 这是来自 RethinkDB 的一个

还有我的纯标头库,它是定制的与回调一起使用。我尝试过Boost协程但是我还没有使用它们,因为 与valgrind。我的实现使用 ucontext.h 并且到目前为止在 valgrind 下运行良好。

对于“标准”协程,您必须跳过一些环节才能将它们与回调一起使用。例如,以下是工作线程安全(但泄漏)的 Cherokee 处理程序在 Boost 协程中的外观:

typedef coroutine<void()> coro_t;
auto lock = make_shared<std::mutex>();
coro_t* coro = new coro_t ([handler,buffer,&coro,lock](coro_t::caller_type& ca)->void {
  p1: ca();  // Pass the control back in order for the `coro` to initialize.
  coro_t* coro_ = coro;  // Obtain a copy of the self-reference in order to call self from callbacks.
  cherokee_buffer_add (buffer, "hi", 2); handler->sent += 2;
  lock->lock();  // Prevents the thread from calling the coroutine while it still runs.
  std::thread later ([coro_,lock]() {
    //std::this_thread::sleep_for (std::chrono::milliseconds (400));
    lock->lock();  // Wait for the coroutine to cede before resuming it.
    (*coro_)();  // Continue from p2.
  }); later.detach();
  p2: ca();  // Relinquish control to `cherokee_handler_frople_step` (returning ret_eagain).
  cherokee_buffer_add (buffer, ".", 1); handler->sent += 1;
});
(*coro)(); // Back to p1.
lock->unlock(); // Now the callback can run.

这是它在我的协程中的外观:

struct Coro: public glim::CBCoro<128*1024> {
  cherokee_handler_frople_t* _handler; cherokee_buffer_t* _buffer;
  Coro (cherokee_handler_frople_t *handler, cherokee_buffer_t* buffer): _handler (handler), _buffer (buffer) {}
  virtual ~Coro() {}
  virtual void run() override {
    cherokee_buffer_add (_buffer, "hi", 2); _handler->sent += 2;
    yieldForCallback ([&]() {
      std::thread later ([this]() {
        //std::this_thread::sleep_for (std::chrono::milliseconds (400));
        invokeFromCallback();
      }); later.detach();
    });
    cherokee_buffer_add_str (_buffer, "."); _handler->sent += 1;
  }
};

There's a bunch of coroutine libraries for C++. Here's one from RethinkDB.

There's also mine header-only library, which is tailored to be used with callbacks. I've tried Boost coroutines but I don't use them yet because of the incompatibility with valgrind. My implementation uses ucontext.h and works fine under valgrind so far.

With "standard" coroutines you have to jump thru some hoops to use them with callbacks. For example, here is how a working thread-safe (but leaking) Cherokee handler looks with Boost coroutines:

typedef coroutine<void()> coro_t;
auto lock = make_shared<std::mutex>();
coro_t* coro = new coro_t ([handler,buffer,&coro,lock](coro_t::caller_type& ca)->void {
  p1: ca();  // Pass the control back in order for the `coro` to initialize.
  coro_t* coro_ = coro;  // Obtain a copy of the self-reference in order to call self from callbacks.
  cherokee_buffer_add (buffer, "hi", 2); handler->sent += 2;
  lock->lock();  // Prevents the thread from calling the coroutine while it still runs.
  std::thread later ([coro_,lock]() {
    //std::this_thread::sleep_for (std::chrono::milliseconds (400));
    lock->lock();  // Wait for the coroutine to cede before resuming it.
    (*coro_)();  // Continue from p2.
  }); later.detach();
  p2: ca();  // Relinquish control to `cherokee_handler_frople_step` (returning ret_eagain).
  cherokee_buffer_add (buffer, ".", 1); handler->sent += 1;
});
(*coro)(); // Back to p1.
lock->unlock(); // Now the callback can run.

and here is how it looks with mine:

struct Coro: public glim::CBCoro<128*1024> {
  cherokee_handler_frople_t* _handler; cherokee_buffer_t* _buffer;
  Coro (cherokee_handler_frople_t *handler, cherokee_buffer_t* buffer): _handler (handler), _buffer (buffer) {}
  virtual ~Coro() {}
  virtual void run() override {
    cherokee_buffer_add (_buffer, "hi", 2); _handler->sent += 2;
    yieldForCallback ([&]() {
      std::thread later ([this]() {
        //std::this_thread::sleep_for (std::chrono::milliseconds (400));
        invokeFromCallback();
      }); later.detach();
    });
    cherokee_buffer_add_str (_buffer, "."); _handler->sent += 1;
  }
};
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文