ISO/IEC C++ China Unofficial

 找回密码
 立即注册

QQ登录

只需一步,快速开始

搜索
查看: 450|回复: 9

[C氵] 非抢占式并发(1):p7简介

[复制链接]

3

主题

20

帖子

202

积分

超级版主

mikonmikonmi

Rank: 8Rank: 8

威望
4
经验
170
贡献
4
发表于 2016-1-1 18:35:17 | 显示全部楼层 |阅读模式
本帖最后由 岩川黑鬼 于 2016-1-2 17:39 编辑

看了抚子的boost协程介绍,觉得自己搞那套玩意儿还是太污。不过也姑且安利一下。

一般来说,开发者习于抢占式的并发。在高级语言语句和/或表达式的层面上,抢占式并发很可能不保证任何串行行为,因为它们的重调度触发很可能来自某个外部定时器。在这一前提下,原子化、临界、工人和池是常见的手法,actor风格的并发也稍稍流行。然而如果我们回顾对并发这话题进行探讨的历史,会发现还有CSP(Communicating Sequential Processes, 通信顺序进程)这样的观念。对Go有所了解的读者或许会想到goroutine, 不过单纯的迫真并发并非CSP的全部。

翻开Hoare本人对CSP的论述,他强调过trace这一概念——也即,并发成分产生的确定轨迹。何处串行、何处并发,应当予以演算推定。

实现并没法做得很好,不过我仍然尝试在C中再现一点goroutine. 这结果是p7.
p7提供用户态的并发。实际的并发由可定义数目的pthread(后称线程)搭载,同一线程上的用户线程(粗糙期间,以后称作协程)在没有被迫使重调度的情况下顺序执行。重调度发生的时机可能有:
  • 用户主动暂时放弃;
  • 阻塞在瘦自旋锁或读写自旋锁上;
  • 阻塞在用户指定的IO上;
  • 阻塞在协程间通信上。
同时,若线程上执行了创建协程的代码,会触发一次主动放弃。

这不是个高效的方案,也不是个框架。它多数时候只并发,但审慎的用户或许能够集中地编写代码,而不必在意消息的集散。
下面给出一个例子(stackdemo.c),它是一个粗糙的回显服务。只是简单例子,因此缓冲区是硬编码尺寸的。

  1. #include    <stdio.h>
  2. #include    "./net_common.h"
  3. #include    <sys/stat.h>
  4. #include    "../servcraft/p7/libp7.h"
  5. #include    "../servcraft/p7/p7_root_alloc.h"
  6. #include    "../servcraft/include/model_alloc.h"

  7. void test_echo(void *arg) {
  8.     intptr_t fd_conn_i64crap = (long long) arg;
  9.     int fd_conn = (int) (fd_conn_i64crap & 0xFFFFFFFF);
  10.     char msg[32];
  11.     memset(msg, 0, sizeof(char) * 32);
  12.     int ret = p7_iowrap_timed(recv, P7_IOMODE_READ, 30000, fd_conn, msg, sizeof(msg), 0);
  13.     switch (ret) {
  14.         case -1:
  15.             printf("internal error\n");
  16.             break;
  17.         case -2:
  18.             printf("p7 timed out\n");
  19.             break;
  20.         default:
  21.             p7_iowrap(send, P7_IOMODE_WRITE, fd_conn, msg, strlen(msg), 0);
  22.     }
  23.     close(fd_conn);
  24. }

  25. void test_timeout(void *arg) {
  26.     long long fd_conn_i64crap = (long long) arg;
  27.     int fd_conn = (int) (fd_conn_i64crap & 0xFFFFFFFF);
  28.     printf("Timed out: %d\n", fd_conn);
  29. }

  30. void test_echo_wrapper(void *arg) {
  31.     p7_coro_concat(test_echo, arg, 2048);
  32. }

  33. void test_startup(void *unused) {
  34.     printf("startup\n");
  35. }

  36. int main(int argc, char *argv[]) {
  37.     __auto_type allocator = p7_root_alloc_get_allocator();
  38.     allocator->allocator_.closure_ = malloc;
  39.     allocator->deallocator_.closure_ = free;
  40.     allocator->reallocator_.closure_ = realloc;
  41.     p7_init(atoi(argv[1]), test_startup, NULL);

  42.     int fd_listen;

  43.     if ((fd_listen = socket(PF_INET, SOCK_STREAM, 0)) == -1) {
  44.         perror("socket");
  45.         exit(1);
  46.     }

  47.     struct sockaddr_in lserv;
  48.     lserv.sin_family = AF_INET;
  49.     lserv.sin_port = htons(8080);
  50.     lserv.sin_addr.s_addr = inet_addr(argv[2]);
  51.     bzero(&(lserv.sin_zero), 8);

  52.     if (bind(fd_listen, (struct sockaddr *) &lserv, sizeof(lserv)) == -1) {
  53.         perror("bind");
  54.         exit(1);
  55.     }
  56.    
  57.     if (listen(fd_listen, 5) == -1) {
  58.         perror("listen");
  59.         exit(1);
  60.     }

  61.     while (1) {
  62.         struct sockaddr_in rcli;
  63.         socklen_t addrlen = sizeof(rcli);
  64.         int fd_conn = p7_iowrap(accept, P7_IOMODE_READ, fd_listen, (struct sockaddr *) &rcli, &addrlen);
  65.         intptr_t fd_conn_i64crap = fd_conn;
  66.         p7_coro_create(test_echo_wrapper, (void *) fd_conn_i64crap, 512);
  67.     }

  68.     return 0;
  69. }
复制代码

头文件net_common.h是个胡乱拼凑的东西:

  1. /* common header for net app */

  2. #ifndef                NET_COMMON_H_
  3. #define                NET_COMMON_H_

  4. #include        <stdio.h>
  5. #include        <stdlib.h>
  6. #include        <string.h>

  7. #include        <unistd.h>
  8. #include        <sys/types.h>
  9. #include        <sys/wait.h>
  10. #include        <errno.h>
  11. #include        <sys/stat.h>
  12. #include        <sys/socket.h>
  13. #include    <arpa/inet.h>
  14. #include        <netinet/in.h>
  15. #include        <netdb.h>

  16. #include        <sys/mman.h>
  17. #include        <sys/epoll.h>

  18. #include        <signal.h>
  19. #include        <fcntl.h>

  20. #endif                // NET_COMMON_H_
复制代码
(之前小耗指出这里有reserved name, 确实如此。去掉前缀的下划线。)

然后我试着编译它。

  1. gcc -o stackdemo stackdemo.c -g -O2 -lp7
复制代码

运行的时候给出线程数和IP地址两个参数。端口也是胡乱编码的8080。
它应当能并发地执行,连接建立之后对一次回显请求的响应要求在30秒内到达,否则视作超时而放弃连接。

这个回显服务做了如下的事情:
  • 启动p7并发,执行监听;
  • accept一个连接,以这个连接上的fd为参数启动一个协程A; 协程A具有512B尺寸的栈,它以2KB栈尺寸启动另一个协程B, 并等待它结束;
  • 协程B试图按30kms超时recv一条消息,并把它发送回去;无论如何,适当地善后。

至少它看上去还算简单……

点评

_NET_COMMON_H_ 这 reserved name 是怎么回事?  发表于 2016-1-1 21:37

评分

参与人数 2威望 +2 贡献 +2 收起 理由
帅气可爱魔理沙 + 1 + 1 meow
LH_Mouse + 1 + 1 先拜。

查看全部评分

沿海征收头GAY骨
回复

使用道具 举报

9

主题

25

帖子

137

积分

注册会员

魔理魔理魔~

Rank: 2

威望
5
经验
97
贡献
5
发表于 2016-1-2 03:12:48 | 显示全部楼层

不明觉厉

点评

我对并发不怎么敏感...  发表于 2016-1-7 00:49
摩罗沙看看go的goroutine.  发表于 2016-1-2 17:41
Yare Yare Daze
回复

使用道具 举报

1

主题

16

帖子

142

积分

注册会员

Rank: 2

威望
0
经验
126
贡献
0
发表于 2016-1-2 16:36:05 | 显示全部楼层
瞻仰留念
回复

使用道具 举报

3

主题

20

帖子

202

积分

超级版主

mikonmikonmi

Rank: 8Rank: 8

威望
4
经验
170
贡献
4
 楼主| 发表于 2016-1-2 17:37:28 | 显示全部楼层
_NET_COMMON_H_ 这 reserved name 是怎么回事?


是我傻逼了。那头文件我大二的时候写的,后来胡乱拷着本地用,几年都忘改header guard...
沿海征收头GAY骨
回复 支持 反对

使用道具 举报

8

主题

64

帖子

269

积分

超级版主

Rank: 8Rank: 8

威望
12
经验
169
贡献
12
发表于 2016-1-6 08:51:48 | 显示全部楼层
终于有官方文档了,先马后读
回复 支持 反对

使用道具 举报

3

主题

74

帖子

283

积分

中级会员

Rank: 3Rank: 3

威望
9
经验
182
贡献
9
发表于 2016-1-6 12:55:31 | 显示全部楼层
斯国一
#if !idppc
/*
** float q_rsqrt( float number )
*/
float Q_rsqrt( float number )
{
        long i;
        float x2
回复

使用道具 举报

3

主题

20

帖子

202

积分

超级版主

mikonmikonmi

Rank: 8Rank: 8

威望
4
经验
170
贡献
4
 楼主| 发表于 2016-1-9 14:36:00 | 显示全部楼层
nadesico19 发表于 2016-1-6 08:51
终于有官方文档了,先马后读

我懒成狗,文档一直没写……
哪天写个man吧_(:з」∠)_
沿海征收头GAY骨
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|手机版|Archiver|ISO/IEC C++ China Unofficial

GMT+8, 2017-12-18 09:28 , Processed in 0.046859 second(s), 23 queries , XCache On.

Powered by Discuz! X3.4

© 2001-2017 Comsenz Inc.

快速回复 返回顶部 返回列表