设为首页 加入收藏

TOP

C语言协程与网络编程分析(一)
2013-01-25 12:54:05 来源: 作者: 【 】 浏览:2102
Tags:语言 程与 网络编程 分析

  协程

  协程,即协作式程序,其思想是,一系列互相依赖的协程间依次使用CPU,每次只有一个协程工作,而其他协程处于休眠状态。协程可以在运行期间的某个点上暂停执行,并在恢复运行时从暂停的点上继续执行。 协程已经被证明是一种非常有用的程序组件,不仅被python、lua、ruby等脚本语言广泛采用,而且被新一代面向多核的编程(www.cppentry.com)语言如golang rust-lang等采用作为并发的基本单位。 协程可以被认为是一种用户空间线程,与传统的线程相比,有2个主要的优点:

  与线程不同,协程是自己主动让出CPU,并交付他期望的下一个协程运行,而不是在任何时候都有可能被系统调度打断。因此协程的使用更加清晰易懂,并且多数情况下不需要锁机制。

  与线程相比,协程的切换由程序控制,发生在用户空间而非内核空间,因此切换的代价非常小。

  网络编程(www.cppentry.com)模型

  首先来简单回顾一下一些常用的网络编程(www.cppentry.com)模型。网络编程(www.cppentry.com)模型可以大体的分为同步模型和异步模型两类。

  同步模型:

  同步模型使用阻塞IO模式,在阻塞IO模式下调用read等IO函数时会阻塞线程直到IO完成或失败。

  同步模型的典型代表是thread per connection模型,每当阻塞在主线程上的accept调用返回时则创建一个新的线程去服务于新的socket的读/写。这种模型的优点是程序简洁,编写简单;缺点是可伸缩性收到线程数的限制,当连接越来越多时,线程也越来越多,频繁的线程切换会严重拖累性能。

  异步模型:

  异步模型一般使用非阻塞IO模式,并配合epoll/select/poll等多路复用机制。在非阻塞模式下调用read,如果没有数据可读则立即返回并通知用户没有可读(EAGAIN/EWOULDBLOCK),而非阻塞当前线程。异步模型可以使一个线程同时服务于多个IO对象。

  异步模型的典型代表是reactor模型。在reactor模型中,我们将所有要处理的IO事件注册到一个中心的IO多路复用器中(一般为epoll/select/poll),同时主线程阻塞在多路复用器上。一旦有IO事件到来或者就绪,多路复用器返回并将对应的IO事件分发到对应的处理器(即回调函数)中,最后处理器调用read/write函数来进行IO操作。

  异步模型的特点是性能和可伸缩性比同步模型要好很多,但是其结构复杂,不易于编写和维护。在异步模型中,IO之前的代码(IO任务的提交者)和IO之后的处理代码(回调函数)是割裂开来的。

  协程与网络编程(www.cppentry.com)

  协程为克服同步模型和异步模型的缺点,并结合他们的优点提供了可能: 现在假设我们有3个协程A,B,C分别要进行数次IO操作。这3个协程运行在同一个调度器或者说线程的上下文中,并依次使用CPU。调度器在其内部维护了一个多路复用器(epoll/select/poll)。

  协程A首先运行,当它执行到一个IO操作,但该IO操作并没有立即就绪时,A将该IO事件注册到调度器中,并主动放弃CPU。这时调度器将B切换到CPU上开始执行,同样,当它碰到一个IO操作的时候将IO事件注册到调度器中,并主动放弃CPU。调度器将C切换到cpu上开始执行。当所有协程都被“阻塞”后,调度器检查注册的IO事件是否发生或就绪。假设此时协程B注册的IO时间已经就绪,调度器将恢复B的执行,B将从上次放弃CPU的地方接着向下运行。A和C同理。

  这样,对于每一个协程来说,是同步的模型;但是对于整个应用程序来说,却是异步的模型。

  好了,原理说完了,我们来看一个实际的例子,echo server。

  echo server

  在这个例子中,我们将使用orchid库来编写一个echo server。orchid库是一个构建于boost基础上的 协程/网络IO 库。

  echo server首先必须要处理连接事件,我们创建一个协程来专门处理连接事件:

  typedef boost::shared_ptr socket_ptr;

  //处理ACCEPT事件的协程 void handle_accept(orchid::coroutine_handle co) {

  try {

  orchid::acceptor acceptor(co -> get_scheduler().get_io_service());//构建一个acceptor

  acceptor.bind_and_listen("5678",true);

  for(;;) {

  socket_ptr sock(new orchid::socket(co -> get_scheduler().get_io_service()));

  acceptor.accept(*sock,co);

   

首页 上一页 1 2 3 4 5 6 7 下一页 尾页 1/9/9
】【打印繁体】【投稿】【收藏】 【推荐】【举报】【评论】 【关闭】 【返回顶部
分享到: 
上一篇C语言协程与网络编程的分析 下一篇c语言调用Python脚本

评论

帐  号: 密码: (新用户注册)
验 证 码:
表  情:
内  容: