欢迎访问Lu程序设计

C/C++使用Lu脚本协程

1 说明

要演示本文的例子,你必须下载Lu32脚本系统。本文的例子需要lu32.dll、lu32.lib、C格式的头文件lu32.h,相信你会找到并正确使用这几个文件。

用C/C++编译器创建一个控制台应用程序,复制本文的例子代码直接编译运行即可。

2 关于Lu协程

在本教程系列的开始,介绍了Lu脚本的基本数据结构(详细参考Lu编程指南),即:

如果Lu表达式(函数)中使用了函数yield,该表达式称为一个协程(coroutine)(详细参考Lu用户指南)。协程不能递归运行,只有这一个限制。Lu协程有4种状态:正常、运行、挂起、 终止。正常态是可以运行但还没有运行的状态;终止态协程是不能运行的,如果运行将返回nil。与协程相关函数有5个:

yield     :挂起协程并立即返回表达式(函数)的值。
status    :查询表达式(包含协程)的状态。
abort     :中止挂起的协程,此时协程处于正常态。
terminate :终止协程,协程处于终止态,不能运行。
resume    :重启一个终止的协程,协程转为正常态。

详细说明:

(1)查询表达式(包含协程)状态 status(p):

p是一个表达式句柄。正常返回一个整数表示表达式状态:0:普通表达式;1:普通表达式正在运行;2:普通表达式递归运行;5:协程;6:协程正在运行;7:协程挂起;8:协程终止。p非法时返回nil。

(2)中止挂起的协程 abort(p):

p是一个协程句柄,正处于挂起状态。协程中止后,可以再次从头开始执行。操作成功返回true,否则返回false。

(3)终止协程 terminate(p):

p是一个协程句柄,处于正常等待运行状态或者挂起状态。协程终止后将不能执行,执行时总返回nil。操作成功返回true,否则返回false。

(4)重启一个终止的协程 resume(p):

p是一个协程句柄,正处于终止状态,重启后可以运行。操作成功返回true,否则返回false。

如果用C/C++编程,可以使用Lu核心库的输出函数ExMsgWithLu的4、8、9、10号功能操作协程。见下面的说明:

void * _stdcall ExMsgWithLu(int Type,void *hMsg,void **Msg1,void **Msg2);    //C定义

Type:输入参数,信息交换的类型。
    hMsg:输入参数,信息句柄,其意义取决于Type。
    Msg1:该参数必须为变量,不能为常量。输入值及返回值取决于Type。
    Msg2:该参数必须为变量,不能为常量。输入值及返回值取决于Type。
    返回值:返回值的意义取决于Type。

说明:与Lu进行信息交换,向Lu传送信息或者从Lu获得信息。信息的类型及意义如下表所示:

Type hMsg Msg1 Msg2 返回值 说明
1 从二级函数获得的表达式句柄

返回指向表达式模块号的指针

返回表达式句柄 指向表达式名称的字符串指针 得到表达式名称、模块号、句柄
2 编译表达式得到的表达式句柄

返回指向表达式模块号的指针

无意义 指向表达式名称的字符串指针 得到表达式名称、模块号
3 luVOID类型指针,模块号 luVOID类型指针,返回模块中的表达式数目 无意义 无意义 得到模块中的表达式数目
4 编译表达式得到的表达式句柄

无意义

无意义 将返回值取整数,表示表达式的状态。 返回值表示表达式状态:0:普通表达式;1:普通表达式正在运行;2:普通表达式递归运行;5:协程;6:协程正在运行;7:协程挂起;8:协程终止。
5 无意义 无意义 无意义 无意义 清空缓冲池
6 luIFOR类型指针,设置对象总数 luIFOR类型指针,返回以前设置的对象总数 无意义 无意义 设置对象(指针键)总数为多少时自动启动垃圾收集器GC。当*hMsg小于等于0时不会自动启动垃圾收集器。
7 函数指针:void (_stdcall *GcEndCallMe)(void); 非0表示设置函数指针,0表示取消设置 无意义 非0表示操作成功。 设置一个函数GcEndCallMe,该函数将在垃圾收集器的最后调用。
8 编译表达式得到的表达式句柄 无意义 无意义 非0表示操作成功。 中止正处于挂起状态的协程。协程中止后,可以再次从头开始执行。
9 编译表达式得到的表达式句柄 无意义 无意义 非0表示操作成功。 终止处于正常等待运行状态或者挂起状态的协程。协程终止后将不能执行,执行时总返回nil。
10 编译表达式得到的表达式句柄 无意义 无意义 非0表示操作成功。 重启一个终止的协程,重启后可以运行。

3 代码

代码1:最简单的协程

#include <stdio.h>
#include "lu32.h"
#pragma comment( lib, "lu32.lib" )
void main(void)
{
void *hFor; //存放表达式句柄,即脚本函数句柄
luINT nPara; //存放表达式的自变量个数
LuData *pPara; //存放输入自变量的数组指针
LuData Val; //存放表达式的值
luINT ErrBegin,ErrEnd; //表达式编译出错的初始位置和结束位置
int ErrCode; //错误代码
wchar_t ForStr[]=L"f(x:i)= i=0, while{(i++, i<=5), yield(i)}, 888"; //字符串表达式
int i;
if(!InitLu()) return; //初始化Lu
ErrCode=LuCom(ForStr,0,0,0,&hFor,&nPara,&pPara,&ErrBegin,&ErrEnd); //编译表达式
if(ErrCode)
{
printf("表达式有错误!错误代码: %d \n",ErrCode);
}
else
{
for(i=0;i<8;i++) //循环调用8次,观察每次调用的结果
{
Val=LuCal(hFor,pPara); //计算表达式的值
if(Val.BType==luStaData_int64) printf("%I64d\n",Val.x);
}
}
FreeLu(); //释放Lu
}

运行结果:

1
2
3
4
5
888
1
2

代码2:协程状态转换

#include <stdio.h>
#include "lu32.h"
#pragma comment( lib, "lu32.lib" )
void main(void)
{
void *hFor; //存放表达式句柄,即脚本函数句柄
luINT nPara; //存放表达式的自变量个数
LuData *pPara; //存放输入自变量的数组指针
LuData Val; //存放表达式的值
luINT ErrBegin,ErrEnd; //表达式编译出错的初始位置和结束位置
int ErrCode; //错误代码
void *Msg1, *Msg2; //与Lu交换信息用
wchar_t ForStr[]=L"f(x:i)= i=0, while{(i++, i<=5), yield(i)}, 888"; //字符串表达式
if(!InitLu()) return; //初始化Lu
ErrCode=LuCom(ForStr,0,0,0,&hFor,&nPara,&pPara,&ErrBegin,&ErrEnd); //编译表达式
if(ErrCode)
{
printf("表达式有错误!错误代码: %d \n",ErrCode);
}
else
{
Val=LuCal(hFor,pPara);
if(Val.BType==luStaData_int64) printf("%I64d\n",Val.x);
Val=LuCal(hFor,pPara);
if(Val.BType==luStaData_int64) printf("%I64d\n",Val.x);
printf("协程状态 %d\n",(int)ExMsgWithLu(4,hFor, &Msg1, &Msg2));
Val=LuCal(hFor,pPara);
if(Val.BType==luStaData_int64) printf("%I64d\n",Val.x);
if(ExMsgWithLu(8,hFor, &Msg1, &Msg2)) printf("协程已中止!\n");
Val=LuCal(hFor,pPara);
if(Val.BType==luStaData_int64) printf("%I64d\n",Val.x);
if(ExMsgWithLu(9,hFor, &Msg1, &Msg2)) printf("协程已终止!\n");
printf("协程状态 %d\n",(int)ExMsgWithLu(4,hFor, &Msg1, &Msg2));
Val=LuCal(hFor,pPara);
if(Val.BType==luStaData_nil) printf("协程返回nil!\n");
if(ExMsgWithLu(10,hFor, &Msg1, &Msg2)) printf("协程已重启!\n");
Val=LuCal(hFor,pPara);
if(Val.BType==luStaData_int64) printf("%I64d\n",Val.x);
Val=LuCal(hFor,pPara);
if(Val.BType==luStaData_int64) printf("%I64d\n",Val.x);
Val=LuCal(hFor,pPara);
if(Val.BType==luStaData_int64) printf("%I64d\n",Val.x);
}
FreeLu(); //释放Lu
}

运行结果:

1
2
协程状态 7
3
协程已中止!
1
协程已终止!
协程状态 8
协程返回nil!
协程已重启!
1
2
3

代码3:处理随机数的协程

#include <stdio.h>
#include <stdlib.h>
#include <locale.h>
#include "lu32.h" #pragma comment( lib, "lu32.lib" ) void _stdcall LuMessage(wchar_t *pch) //输出动态库信息,该函数注册到Lu,由Lu二级函数调用
{
wprintf(L"%s",pch);
} //Lu脚本可调用的二级函数定义
LuData _stdcall lu_rand(luINT mm,LuData *xx,void *vFor) //生成0~9的随机数,通过参数返回
{
xx[0].BType=luStaData_int64; xx[0].VType=luStaData_int64; xx[0].x = rand()%10;
xx[1].BType=luStaData_int64; xx[1].VType=luStaData_int64; xx[1].x = rand()%10;
return *xx;
} void main(void)
{
void *hSend,*hReceive1,*hReceive2; //存放表达式句柄,即脚本函数句柄
luINT nPara; //存放表达式的自变量个数
LuData *pPara; //存放输入自变量的数组指针
LuData Para[2]; //存放表达式的参数
luINT ErrBegin,ErrEnd; //表达式编译出错的初始位置和结束位置
luVOID k=0; //32位平台上luVOID被定义为__int32;64位平台上luVOID被定义为__int64;k必须赋值为0
void *Msg1, *Msg2; //与Lu交换信息用
wchar_t send[]=L"send(x,y)= while{true, rand(&x,&y), o{\"\\r\\n*** send : \", x, \" \", y}, yield(0)}"; //生成数据的脚本函数
wchar_t receive1[]=L"receive1(x,y:a)= a=0, while{(a=a+(x+y), a< 30 ), o{\"\\r\\n+++ receive1 : \", a}, yield(0)}"; //处理数据的脚本函数
wchar_t receive2[]=L"receive2(x,y:a)= a=0, while{(a=a+(x-y), a>(-10)), o{\"\\r\\n--- receive2 : \", a}, yield(0)}"; //处理数据的脚本函数 srand(1); //设置随机数的种子,改变该值可演示不同结果 setlocale(LC_ALL, "chs"); //设置可以输出中文 if(!InitLu()) return; //初始化Lu InsertKey((char *)&k,sizeof(luVOID),luPubKey_User,LuMessage,NULL,NULL,1,&Msg1); //使Lu运行时可输出函数信息 SetFunction(L"rand",lu_rand,1); //设置二级函数,有2个自变量参数 if( LuCom(send, 0,0,0,&hSend, &nPara,&pPara,&ErrBegin,&ErrEnd) || //编译表达式
LuCom(receive1,0,0,0,&hReceive1,&nPara,&pPara,&ErrBegin,&ErrEnd) ||
LuCom(receive2,0,0,0,&hReceive2,&nPara,&pPara,&ErrBegin,&ErrEnd))
{
printf("表达式有错误!\n");
}
else
{
while(1)
{
LuCal(hSend,Para); //运行协程hSend,产生数据,数据由Para返回 LuCal(hReceive1,Para); //运行协程hReceive1,处理数据
if((int)ExMsgWithLu(4,hReceive1, &Msg1, &Msg2)==5) //如果协程hReceive1结束
{
ExMsgWithLu(9,hReceive1, &Msg1, &Msg2); //终止协程hReceive1
} LuCal(hReceive2,Para); //运行协程hReceive2,处理数据
if((int)ExMsgWithLu(4,hReceive2, &Msg1, &Msg2)==5) //如果协程hReceive2结束
{
ExMsgWithLu(9,hReceive2, &Msg1, &Msg2); //终止协程hReceive2
} //如果协程hReceive1和hReceive2都终止
if((int)ExMsgWithLu(4,hReceive1, &Msg1, &Msg2)==8 && (int)ExMsgWithLu(4,hReceive2, &Msg1, &Msg2)==8)
{
ExMsgWithLu(9,hSend, &Msg1, &Msg2); //终止协程hSend
break;
}
}
} FreeLu(); //释放Lu
}

运行结果:

*** send : 1 7
+++ receive1 : 8
--- receive2 : -6
*** send : 4 0
+++ receive1 : 12
--- receive2 : -2
*** send : 9 4
+++ receive1 : 25
--- receive2 : 3
*** send : 8 8
--- receive2 : 3
*** send : 2 4
--- receive2 : 1
*** send : 5 5
--- receive2 : 1
*** send : 1 7
--- receive2 : -5
*** send : 1 1
--- receive2 : -5
*** send : 5 2
--- receive2 : -2
*** send : 7 6
--- receive2 : -1
*** send : 1 4
--- receive2 : -4
*** send : 2 3
--- receive2 : -5
*** send : 2 2
--- receive2 : -5
*** send : 1 6

4函数说明

本例用到了Lu的7个输出函数:初始化Lu的函数InitLu,释放Lu的函数FreeLu,编译表达式的函数LuCom、计算表达式的函数LuCal、注册C/C++函数的函数SetFunction、插入键值函数InsertKey、与Lu交换信息函数ExMsgWithLu。从这里查看这些函数的说明:Lu编程指南

5 难点分析

代码1和代码2较简单,这里仅对代码3做一下说明。

代码3中用C/C++定义了一个函数lu_rand,注册到Lu系统由脚本调用(函数名为rand),用以产生随机数。另外,定义的3个脚本函数功能如下:

send(x,y)= while{true, rand(&x,&y), o{"\r\n*** send : ", x, " ", y}, yield(0)}    //不断产生并输出随机数,然后挂起协程,随机数由参数x和y返回

receive1(x,y:a)= a=0, while{(a=a+(x+y), a< 30 ), o{"\r\n+++ receive1 : ", a}, yield(0)}   //当执行a=a+(x+y), a< 30  时,输出a,然后挂起协程

receive2(x,y:a)= a=0, while{(a=a+(x-y), a>(-10)), o{"\r\n--- receive2 : ", a}, yield(0)}  //当执行a=a+(x-y), a<(-10)时,输出a,然后挂起协程

6 其他

你可能注意到了,我的联系方式就在下面,如有不明之处或有什么建议,可随时与我进行联系。


版权所有© Lu程序设计 2002-2013,保留所有权利
E-mail: forcal@sina.com  QQ:630715621
最近更新: 2014年01月07日

C/C++使用Lu脚本协程的更多相关文章

  1. Unity脚本编程之——协程(Coroutine)

    本文翻译自Unity官方文档:https://docs.unity3d.com/Manual/Coroutines.html 专有名词: Coroutine 协程 Alpha 不透明度 当你调用一个函 ...

  2. lu协程练习

    生产者和消费者问题:当协程调用yield时,从一个悬而未决的resume中返回.简单的协程练习: function receive() local status,value = coroutine.r ...

  3. Unity学习疑问记录之协程

    http://blog.csdn.net/huang9012/article/details/38492937 总结:1.协程相当于多线程但不是,(尽管它们看上去是这样的),它们运行在同一线程中,跟普 ...

  4. python之协程与IO操作

    协程 协程,又称微线程,纤程.英文名Coroutine. 协程的概念很早就提出来了,但直到最近几年才在某些语言(如Lua)中得到广泛应用. 子程序,或者称为函数,在所有语言中都是层级调用,比如A调用B ...

  5. Lua 协程coroutine

    协程和一般多线程的区别是,一般多线程由系统决定该哪个线程执行,是抢占式的,而协程是由每个线程自己决定自己什么时候不执行,并把执行权主动交给下一个线程. 协程是用户空间线程,操作系统其存在一无所知,所以 ...

  6. Unity3D之协程(Coroutines & Yield )

    在Unity中StartCoroutine/yield return这个模式到底是怎么应用的? 比如你要一个方法进行一个比较耗时的复杂运算~同时又想让脚本流畅的进行其他操作而不是卡在那里等该方法执行完 ...

  7. [转]-Lua协程的实现

    协程是个很好的东西,它能做的事情与线程相似,区别在于:协程是使用者可控的,有API给使用者来暂停和继续执行,而线程由操作系统内核控制:另 外,协程也更加轻量级.这样,在遇到某些可能阻塞的操作时,可以使 ...

  8. 多线程、多进程、协程、缓存(memcache、redis)

    本节内容: 线程: a:基本的使用: 创建线程: 1:方法 import threading def f1(x): print(x) if __name__=='__main__': t=thread ...

  9. Unity协程(Coroutine)原理深入剖析

    Unity协程(Coroutine)原理深入剖析 By D.S.Qiu 尊重他人的劳动,支持原创,转载请注明出处:http.dsqiu.iteye.com 其实协程并没有那么复杂,网上很多地方都说是多 ...

随机推荐

  1. javascript动态创建对象

    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/ ...

  2. IT运维外包甩不掉的包袱

    对一个企业的IT信息部门来说,保证IT系统的安全.稳定和可靠运行是IT部门义不容辞的职责,但IT系统的安全.稳定和可靠是相对的,得看企业IT投入和ROI.现在企业的IT系统运维面临着多重压力:一方面是 ...

  3. GIS-开发例程、源代码、MapXtreme、Map (转)

    [原创]MapXtreme实用技巧与源码10例 普通图片生成MapInfo格式电子地图的步骤  http://blog.csdn.net/hmbb2008/category/184134.aspx 基 ...

  4. JavaScript事件属性绑定带参数的函数

    JavaScript中在对事件进行绑定的时候,往往是element.onclick=event;这种形式,这样使用的话则会出现无法传参数.因此我们可以使用function(){}匿名函数将事件包含其中 ...

  5. JavaMail简单接收邮件

    一个简单的例子,收取所有邮件并在控制台输出. package cn.jmail.test; import java.io.*; import java.util.*; import javax.mai ...

  6. Core Java读书笔记之String

    Java里面的String Conceptually, Java Strings are sequences of Unicode characters. Java里面的String都是Unicode ...

  7. windows本地搭建grunt前端项目构建环境

    初学,目前对grunt的理解和需求仅在于简单的文件合并.压缩.语法检查,其强大功能还有待研究. 安装前环境准备 (1)grunt依赖nodejs运行环境,所以要玩grunt得先把nodejs安装好,n ...

  8. python 文件移动(shutil)

    # encoding=utf-8 # /home/bergus/tongbu/360共享/编程语言 # /home/bergus/桌面 # /home/bergus/test/hh import os ...

  9. MYSQL 中的变量

    1.用户自己定义变量 2.系统变量(全局变量,会话变量) ----------------------------------------------------------------------- ...

  10. 一个简单的倒计时js插件

    接收的参数end是必须传的,格式是/分隔的日期字符串,start是可选的,不传就是从现在开始倒计时,callback也是可选的,到倒计时接收时执行自定义的函数. countdown({ 'end':' ...