servcraft/p7是使用C实现的stackful协程库,提供“半”透明的CSP并发。
目前,servcraft只支持Linux/GCC/pthread. 由于阻塞IO封装使用了epoll, 推荐使用Linux 2.6以上。
若要利用servcraft提供的GCC闭包功能,请使用主版本号不低于5的GCC.
servcraft/p7所使用的协程(coroutines, coro)提供类似goroutine的功能。
编写并发程序的过程较一般事件驱动框架更简单。每个协程假定自己和其他协程一样,被并发地调度且独立地执行,因而可以顺序编写逻辑。
为使用文件描述符的系统调用提供了IO阻塞-重调度接口。协程阻塞在IO操作上时自动暂停执行、引发重调度,IO就绪时重新获得调度权。
提供Actor风格的协程间通信。协程可以注册自身名字,并按注册的协程名向其他协程的内建邮箱中(零拷贝地)投递信息。
所有IO和协程间通信的阻塞接口都自带超时定时器。
提供协程间瘦自旋锁和读写自旋锁。读写自旋锁使用尽量公平对待读者、写者的机制。
简介和简单demo可以在这里获得。一个粗暴的echo server如下:
#include <stdio.h> #include "./net_common.h" // common header for necessary network APIs #include <sys/stat.h> #include "../servcraft/p7/libp7.h" #include "../servcraft/p7/p7_root_alloc.h" #include "../servcraft/include/model_alloc.h" void test_echo(void *arg) { intptr_t fd_conn_i64crap = (long long) arg; int fd_conn = (int) (fd_conn_i64crap & 0xFFFFFFFF); char msg[32]; memset(msg, 0, sizeof(char) * 32); int ret = p7_iowrap_timed(recv, P7_IOMODE_READ, 30000, fd_conn, msg, sizeof(msg), 0); switch (ret) { case -1: printf("internal error\n"); break; case -2: printf("p7 timed out\n"); break; default: p7_iowrap(send, P7_IOMODE_WRITE, fd_conn, msg, strlen(msg), 0); } close(fd_conn); } void test_timeout(void *arg) { long long fd_conn_i64crap = (long long) arg; int fd_conn = (int) (fd_conn_i64crap & 0xFFFFFFFF); printf("Timed out: %d\n", fd_conn); } void test_echo_wrapper(void *arg) { p7_coro_concat(test_echo, arg, 2048); } void test_startup(void *unused) { printf("startup\n"); } int main(int argc, char *argv[]) { __auto_type allocator = p7_root_alloc_get_allocator(); allocator->allocator_.closure_ = malloc; allocator->deallocator_.closure_ = free; allocator->reallocator_.closure_ = realloc; struct p7_init_config config; config.namespace_config.namespace_size = 1024; config.namespace_config.rwlock_granularity = 10; config.namespace_config.spintime = 400; config.pthread_config.nthreads = atoi(argv[1]); config.pthread_config.at_startup = test_startup; config.pthread_config.arg_startup = NULL; p7_init(config); int fd_listen; if ((fd_listen = socket(PF_INET, SOCK_STREAM, 0)) == -1) { perror("socket"); exit(1); } struct sockaddr_in lserv; lserv.sin_family = AF_INET; lserv.sin_port = htons(8080); lserv.sin_addr.s_addr = inet_addr(argv[2]); bzero(&(lserv.sin_zero), 8); if (bind(fd_listen, (struct sockaddr *) &lserv, sizeof(lserv)) == -1) { perror("bind"); exit(1); } if (listen(fd_listen, 5) == -1) { perror("listen"); exit(1); } while (1) { struct sockaddr_in rcli; socklen_t addrlen = sizeof(rcli); int fd_conn = p7_iowrap(accept, P7_IOMODE_READ, fd_listen, (struct sockaddr *) &rcli, &addrlen); intptr_t fd_conn_i64crap = fd_conn; p7_coro_create(test_echo_wrapper, (void *) fd_conn_i64crap, 512); } return 0; }
与子程序(或者说函数)一样,协程(coroutine)也是一种程序组件。Donald Knuth 曾说,子程序是协程的特例。 一个子程序就是一次函数调用,它只有一个入口,一次返回,调用顺序是明确的。但协程的调用和子程序则大不一样,协程允许有多个入口对程序进行中断、继续执行等操作。 Python2 可以通过 yield 来实现基本的协程,但不够强大,第三方库 gevent 对协程提供了强大的支持。另
Tornado 中推荐用 协程 来编写异步代码. 协程使用 Python 中的关键字 yield 来替代链式回调来实现挂起和继续程序的执行(像在 gevent 中使用的轻量级线程合作的方法有时也称作协程, 但是在 Tornado 中所有协程使用异步函数来实现的明确的上下文切换). 协程和异步编程的代码一样简单, 而且不用浪费额外的线程, . 它们还可以减少上下文切换 让并发更简单 . Exampl
defer 协程客户端的对象结构体,设置client->defer = 1表示启用了defer延迟收包 设置client->defer_yield = 1表示进入了wait状态 事件监听 因为swoole底层的EventLoop总是在运行的,因此可能某个协程客户端没有yield也会收到包。底层需要对数据进行缓存。 Client 自动保存到ccp->result内存中。为了避免收到的数据过多,导致内
概念 Hyperf 是运行于 Swoole 4 的协程之上的,这也是 Hyperf 能提供高性能的其中一个很大的因素。 PHP-FPM 的运作模式 在聊协程是什么之前,我们先聊聊传统 PHP-FPM 架构的运作模式,PHP-FPM 是一个多进程的 FastCGI 管理程序,是绝大多数 PHP 应用所使用的运行模式。假设我们使用 Nginx 提供 HTTP 服务(Apache 同理),所有客户端发起
Python的协程很像生成器,但并不是生成数据,大多数时候扮演了数据消费者的作用。换句话说,协程是一个在每次使用send方法发送数据后就会被唤醒的函数。 协程的要点是将“yield”关键字写在表达式的右边。下面是一个打印出所发送的值的协程例子: def coroutine(): print('My coroutine') while True: val = yiel
就一个简单实现的语言来说,如果有并发需求,像之前说的直接使用宿主环境的线程,加上必要的调度控制即可实现需求,但如果要求比较高,触发到上篇讲的线程和单线程异步的相关缺陷,一个较普遍的解决办法是采用用户态并发,即对于os内核来说,进程只有一个或少数几个线程,而对于源语言来说,接口和使用线程别无二致,由虚拟机实现对这些“线程”的调度,虚拟机的实现则可以一定程度简化、优化调度算法和内存占用,从而达到高并发