先总结一下。
线程是最容易编写的并发方式,操作系统也提供了最好的支持;协程可以做到更强的并发能力,但需要实现调度器;回调是开销最小的,它本来不是特别为并发来设计的,它是通用的异步化操作的实现模型。
注意线程和协程本身也是使用异步来模拟同步,线程由操作系统来模拟,协程由用户级调度器模拟。模拟的过程是:发起事件请求、挂起当前执行过程(线程或协程)、响应事件请求、恢复挂起的执行过程。回调没有这么复杂,你需要自己把连续的执行过程分解成多步操作。
线程就不讨论了,用起来比较简单;协程之前简单研究了一下,切换开销比线程有很大改进,但还是有点大,用作IO事件调度还可以,粒度更小的操作就显得开销过大了,想象一下极端情况下每次调度只处理一字节;回调看起来是个不错的方式,但也有许多问题需要解决。
以上次的代码为例,这里简化一下:
void server_loop(int fd) {
while(true) {
register_read_event(fd);
wait_read_event();
int client = accept(fd);
close(client);
}
}
改成对应的callback方式,为了让代码更像是工具转过来的,我把上一篇的代码修改一下:
void server_loop(int fd) {
register_read_event(fd);
wait_read_event({
int clientfd = accept(fd);
close(clientfd);
server_loop(fd);
});
}
这里不打算讨论while循环的转换问题,虽然这也是个难题,但可以通过设置一个变量来避开。
看一下循环里面是如何转换的?只要把wait_read_event后面的代码放进一个closure就可以了。看起来很简单,如果可以通过宏或其它语言设施通知编译器把某行后面的代码构造一个代码块,的确是会简单一些,不过目前我还不知道有哪种语言可以这样。如果没有语言可以做到这样(或者你用的语言做不到),像上面一样手工构造这个代码块也是可以接受的,毕竟它提供了高性能的并发模式。
问题还没有彻底解决,构造这个代码块实际是撕裂了整个逻辑,想象一下,如果要把register_read_event/wait_read_event和accept封装起来,隐藏这个实现过程,结果要类似这样:
void server_loop(int fd) {
int client = register_read_event_AND_wait_AND_accept(fd);
close(client);
server_loop(fd);
}
问题来了,由于不是线程和协程模式,而是异步回调模式,这么写是不可能的,你只能再构造一个代码块:
void register_read_event_AND_wait_AND_accept(int fd, void delegate(int) dg) {
register_read_event(fd);
wait_read_event({
int client = accept(fd);
dg(client);
}
}
void server_loop(int fd) {
register_read_event_AND_wait_AND_accept(fd, (int client){
close(client);
server_loop(fd);
}
}
原以为把回调过程封装起来,以后直接调用就漂亮了,但结果却是每一次包装都没有减少它,你的逻辑必须做成closure传递进去,脱离不了这种不连贯的方式。
或许让编译器来转换?还是写成这样:
void server_loop(int fd) {
int client = register_read_event_AND_wait_AND_accept(fd);
close(client);
server_loop(fd);
}
它自动转成:
void server_loop(int fd) {
register_read_event_AND_wait_AND_accept(fd, (int client){
close(client);
server_loop(fd);
}
}
如果修改编译器或许能让它做到,把register_read_event_AND_wait_AND_accept声明成asynchronized,它就自动把该调用以及之后的代码进行转换。或者只要把wait_read_event这样声明,所有调用它的方法都自动把这个属性向外传播。。。。。。
一种新的语言?