Re [CPyUG] Coroutine协程和闭包本质上有什么区别么

协程与闭包的关系,就好比是鸡蛋和灌饼,你说它俩本质上有什么区别?

我建议楼主如果对概念感兴趣,可以参考wiki百科关于 coroutine和闭包的条目
http://en.wikipedia.org/wiki/Closure_%28computer_programming%29
http://en.wikipedia.org/wiki/Coroutine
而不是在这里瞎猜瞎联想。

至于Python与Lua的协程,可以参考我翻译的这篇

【译文】比较Lua协程与Python生成器


要注意的是Python本身没有协程概念(第三方实现那就不算本身),只有Generator。

就我个人浅见,闭包概念比较容易理解使用,而协程是一个动态交互的动作,涉及到主程序和协程子程序之间的交换,理解起来要麻烦多多。
跟闭包相关联的概念不是协程,而是first class function。
而与协程相关的,多是“并发”这个概念。

关于协程,有一篇论文非常值得参考
www.inf.puc-rio.br/~roberto/docs/MCC15-04.pdf
是Lua作者写的,建议感兴趣的朋友看看。
至于闭包,看完维基百科条目,基本就应该了解的差不多了

Debian server optimization and run lua wsapi as fastcgi

step1) check ulimit -a (see open files)
/etc/security# vim limits.conf
add:
* soft noproc 36000
* hard noproc 36000
* soft nofile 655360
* hard nofile 655360

step2) use nginx and spawn-fcgi for lua
document: http://forum.slicehost.com/comments.php?DiscussionID=4523
http://davelahn.com/lua-nginx-fastcgi-in-debian
http://jim.studt.net/depository/index.php/setting-up-nginx-and-lua-fastcgi-on-debian-squeeze

command like:
spawn-fcgi -F 4 -u www-data -g www-data -a 127.0.0.1 -p 9099 -P /var/run/nginx-lua-fcgi.pid — /usr/local/bin/wsapi.fcgi

lua wsapi should install by latest luarocks.

step3) nginx.conf
add following (9099 is assigned in step2):
location ~ \.lua$ {
fastcgi_pass 127.0.0.1:9099;
fastcgi_index index.lua;
fastcgi_param SCRIPT_FILENAME /var/www/nginx-default$fastcgi_script_name;
include fastcgi_params;
}

递归实验-C语言递归调用的极限

C语言递归调用不是无限的,当递归到一定时候,会出现stack over flow的问题。http://en.wikipedia.org/wiki/Stack_buffer_overflow

但是,这个度是多少呢?

我构造了一个极为简单的C语言递归程序,大家可以参考这里https://gist.github.com/749543

#include "stdafx.h"
      int count = 0;
void f()
{
  int a = 1; int b = 1; int c = 1; int d = 1;
  a = b + 3;
  c = count + 4;
  d = count + 5*c;
  count++;
  printf("count: %d\n", count);
  f();
}

int _tmain(int argc, _TCHAR* argv[])
     {
        f(); return 0;
      }

我使用的是VC2010SP1的C语言模式编译,console程序,无优化。

运行结果如下:

默认情况下,count最后结果为42860,然后程序就结束了。通过查询MSDN可以知道

http://msdn.microsoft.com/en-us/library/8cxs58a6%28v=vs.80%29.aspx

我们可以通过修改project的link选项中的stack commit size和stack reserve size来改变程序的stack大小。

如果这两个size都设置为1、64、640、2048这几个值,结果都是10092.

如果size设置为1024000,那么结果是42860,与默认情况相同。可以看出这个size是以BYTE为单位。

如果size设置为1048000,结果为43372.

size设置为1072000以及2048000,结果都是86551.

size设置为2110000以及3072000,结果都是130242.

一般来说,我们基本上不需要考虑修改这个大小,因为很少会有将近四万的递归层次,默认值已经足够用了。但是如果万一不够用或者有这种递归需求,那么就需要修改这两个值,另外当我们新建一个线程的时候,也可以编程修改线程stack的大小。

http://cs.nyu.edu/exact/core/doc/stackOverflow.txt 这里解释了一下不同平台上stackoverflow的问题,有一些数据可以参考。

http://stackoverflow.com/questions/53827/checking-available-stack-size-in-c 在这里有人提供了一个有趣的递归C代码片段来判断栈大小。

在这里多废话几句,关于Lua的proper tail call http://www.lua.org/pil/6.3.html 。当我们使用Lua for windows构造一个简单的递归程序运行,依然是会得到stack over flow的结果。但是当我们使用proper tail call这种方式调用,(由于抛弃了前面程序的栈)调用可以无限循环下去,所以Lua这个特性常常用来构造state machine。关于使用proper tail call来实现斐波拉契,可以参考这里http://lua-users.org/wiki/ProperTailRecursion

C语言中本地变量local variable的作用域与生存期

《狂人C》中191页提到“每次运行到auto变量j所在block,会为j寻找存储空间,离开j所在代码模块,j的内存被释放掉。

这是不正确的。

结论应该是:对于C语言而言,本地变量会在栈开始处申请,栈销毁时结束生命。但是本地变量的作用域与所在block相关。之所以编译不通过,是因为这种block之外访问block之内变量的语法是错误的,离开本地变量所在block{},它的作用域无效,但不是说销毁了。

作用域错误是语法层面的。而生存期(存在销毁)与程序栈相关,是运行时的概念,它们是两码事。而且,有没有auto这个keyword修饰,结果都一样,所以auto用不用没有任何意义。

关于C语言程序栈以及调用规范,可以参考我前面翻译的文章:http://sunxiunan.com/?p=1229 也可以查找wiki关于calling conversion方面的资料。

下面我们通过这段代码说明一下:

https://gist.github.com/748095

int main (int argc, char *argv[])

{

 int a = 99;

 int b = a;

 int* pLocala = NULL;

 int* pLocalb = NULL;

 int d = 1;


 printf("hello world \n");

 do{

 int a = 3;

 int b = 23;

 pLocala = &a;

 pLocalb = &b;

 b = a + 5;

 printf("%d %p\n", b, &a);

 }while(0);


 int e = 1;


 b = a;

 printf("%d %p\n", b, &a);

 printf("%p %p %p %p %p %p %p %p\n", &e, pLocalb, pLocala, &d, &pLocalb, &pLocala, &b, &a);

 return(0);

}

如果你运行这段代码(无论是VC++还是GCC),最后printf出来的结果应该是连续的,而&pLocala和&pLocalb其实就是一种while循环中定义的变量a和b所在地址,可以看出来它们夹在变量d与e中间。

我们可以用汇编代码看的更清楚一些,在VC2010生成的汇编代码(C源代码与前面略有不同),完整输出参考 https://gist.github.com/748116

————————————————

;    COMDAT _wmain

_TEXT    SEGMENT

_b$4410 = -32                        ; size = 4

_a$4409 = -20                        ; size = 4

_a$ = -8                        ; size = 4

_argc$ = 8                        ; size = 4

_argv$ = 12                        ; size = 4

; 7    : {

……

; 8    :     int a = 0;

mov    DWORD PTR _a$[ebp], 0

$LN3@wmain:

; 9    :

; 10   :     do

; 11   :     {

; 12   :         auto int a = 3;

mov    DWORD PTR _a$4409[ebp], 3

; 13   :         auto int b = 0;

mov    DWORD PTR _b$4410[ebp], 0

……

——————————————

可以看到中间;12 : auto int a = 3;使用的是这样的汇编代码

mov    DWORD PTR _a$4409[ebp], 3

很显然_a$4409是在程序栈开始位置定义,而不是书中提到的”每次进入block时定义”。

此文发在CU后得到不少有价值的评论

http://bbs.chinaunix.net/thread-1834663-2-1.html

果然如我所料,如果没有O1这个编译选项,VC++2010是不会压缩空间的。
如果加了O1这个”mini space”优化选项,出来的结果也不是OwnWaterloo提到的,临时变量都没有了,优化得很彻底。
我用的是VC2010,过早的优化果然罪恶!(开个玩笑)

无论如何,还是要谢谢OwnWaterloo的说法:勿以汇编释语言。
其实我也同意这个看法,所以尽量以K&R来作为解读文本。
但是对于一些实现相关的内容,如果还有通过自身的解释或者对标准的注解来做说明,那是非常费劲的。
《狂人C》中对++前后缀方式的说明就是如此,尽管键盘农夫试图把++解释的更为通俗易懂深入浅出,但是我看了还是一头雾水。
而当我看过++代码生成的“无优化VC++2010特别为了注释”版本以后,就恍然大悟,源码之下别无秘密。
而且,如果没有我用汇编展示的这个例子,OwnWaterloo大侠也不会出手解释,从这点来讲,也是抛砖引玉。

当然也可以说我这是一种误读,太细化,系统及平台依赖太强,
但是我感觉用汇编来解读部分C语言实现相关的细节,在现阶段对我而言是有帮助的,那就可以了。

int _tmain(int argc, _TCHAR* argv[])
{
int a = 0;
do
{
char buf[1024];
buf[0] = 1;
}while (0);

a = 3;

do
{
char buf[1024];
buf[0] = 93;
}while(0);

return 0;
}

ASM输出结果为:

_TEXT        SEGMENT
_buf$4413 = -2076                                        ; size = 1024
_buf$4409 = -1044                                        ; size = 1024
_a$ = -12                                                ; size = 4
__$ArrayPad$ = -4                                        ; size = 4
_argc$ = 8                                                ; size = 4
_argv$ = 12                                                ; size = 4
_wmain        PROC                                                ; COMDAT

; 7    : {

push        ebp
mov        ebp, esp
sub        esp, 2272                                ; 000008e0H
push        ebx
push        esi
push        edi
lea        edi, DWORD PTR [ebp-2272]
mov        ecx, 568                                ; 00000238H
mov        eax, -858993460                                ; ccccccccH
rep stosd
mov        eax, DWORD PTR ___security_cookie
xor        eax, ebp
mov        DWORD PTR __$ArrayPad$[ebp], eax

; 8    :         int a = 0;

mov        DWORD PTR _a$[ebp], 0
$LN6@wmain:

; 9    :
; 10   :         do
; 11   :         {
; 12   :                 char buf[1024];
; 13   :                 buf[0] = 1;

mov        BYTE PTR _buf$4409[ebp], 1

; 14   :         }while (0);

xor        eax, eax
jne        SHORT $LN6@wmain

; 15   :
; 16   :         a = 3;

mov        DWORD PTR _a$[ebp], 3
$LN3@wmain:

; 17   :
; 18   :         do
; 19   :         {
; 20   :                 char buf[1024];
; 21   :                 buf[0] = 93;

mov        BYTE PTR _buf$4413[ebp], 93                ; 0000005dH

; 22   :         }while(0);