<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Nutty Coder &#187; C</title>
	<atom:link href="http://blog.nuttycoder.com/tag/c/feed/" rel="self" type="application/rss+xml" />
	<link>http://blog.nuttycoder.com</link>
	<description>tech articles</description>
	<lastBuildDate>Wed, 08 Dec 2010 13:26:04 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.1</generator>
		<item>
		<title>Lua相关问题整理（2） &#8211; 如何在C中为Lua提供同步调用接口</title>
		<link>http://blog.nuttycoder.com/2010/11/08/lua%e7%9b%b8%e5%85%b3%e9%97%ae%e9%a2%98%e6%95%b4%e7%90%86%ef%bc%882%ef%bc%89/</link>
		<comments>http://blog.nuttycoder.com/2010/11/08/lua%e7%9b%b8%e5%85%b3%e9%97%ae%e9%a2%98%e6%95%b4%e7%90%86%ef%bc%882%ef%bc%89/#comments</comments>
		<pubDate>Mon, 08 Nov 2010 04:38:37 +0000</pubDate>
		<dc:creator>Benny Chen</dc:creator>
				<category><![CDATA[Lua]]></category>
		<category><![CDATA[游戏开发 | Game Programming]]></category>
		<category><![CDATA[C]]></category>
		<category><![CDATA[coroutine]]></category>
		<category><![CDATA[login]]></category>
		<category><![CDATA[lua]]></category>
		<category><![CDATA[resume]]></category>
		<category><![CDATA[yield]]></category>
		<category><![CDATA[同步调用]]></category>

		<guid isPermaLink="false">http://blog.nuttycoder.com/?p=223</guid>
		<description><![CDATA[这个问题的具体描述是——C注册给Lua一个函数，但Lua调用该C函数并不能立即获得结果（比如需要访问远程服务器获取值），如何能让Lua停止并等待，直到获取到结果后，才继续执行接下来的脚本。 举个例子，比如说有一个C函数login，我们试图通过调用该函数以执行用户的登录操作并获取验证结果。首先看下面这段C代码： // C code int login( lua_State *L ) { string user = luaL_checkstring( L, 1 ); string password = luaL_checkstring( L, 2 ); // 该函数将user和password发送到服务器后立即返回， // 绝不要在此处阻塞，这将严重影响效率 sendAuthenticationInfo( user, password ); return 0; } // 注册给Lua lua_register( L, &#34;login&#34;, login ); 可以看到，因为该函数需要访问远程的登录服务器，在系统中一般都采取异步操作（让系统阻塞等待结果是不可接受的）。但是这样Lua开发人员调用login函数时，也只能异步等待结果，下面是Lua中处理登录操作的代码。 -- Lua code -- 当用户点击“登陆”按钮后，执行该函数 function onClickLoginBtn() local user = ... [...]]]></description>
			<content:encoded><![CDATA[<p>这个问题的具体描述是——C注册给Lua一个函数，但Lua调用该C函数并不能立即获得结果（比如需要访问远程服务器获取值），如何能让Lua停止并等待，直到获取到结果后，才继续执行接下来的脚本。</p>
<p>举个例子，比如说有一个C函数login，我们试图通过调用该函数以执行用户的登录操作并获取验证结果。首先看下面这段C代码：</p>
<pre class="brush: cpp; title: ;">
// C code
int login( lua_State *L )
{
	string user = luaL_checkstring( L, 1 );
	string password = luaL_checkstring( L, 2 );

	// 该函数将user和password发送到服务器后立即返回，
	// 绝不要在此处阻塞，这将严重影响效率
	sendAuthenticationInfo( user, password );

	return 0;
}
// 注册给Lua
lua_register( L, &quot;login&quot;, login );
</pre>
<p>可以看到，因为该函数需要访问远程的登录服务器，在系统中一般都采取异步操作（让系统阻塞等待结果是不可接受的）。但是这样Lua开发人员调用login函数时，也只能异步等待结果，下面是Lua中处理登录操作的代码。</p>
<pre class="brush: jscript; title: ;">
-- Lua code
-- 当用户点击“登陆”按钮后，执行该函数
function onClickLoginBtn()
	local user = ...
	local password = ...
	login( user, password ) -- 只是发送login命令
end

--当获取login结果后的回调函数
function onGetLoginResult( bPass )
	if bPass == true then
		print( &quot;Authentication succeeded.&quot; )
		...omitted code...
	else
		print( &quot;Authentication failed.&quot; )
		...omitted code...
	end
end
</pre>
<p>这段Lua代码很好理解，首先onClickLoginBtn是一个按钮事件处理函数，当用户点击了”登录“按钮后，会触发该函数。该函数首先从界面上获取用户输入的user和password，然后调用了上面C中所定义的login函数。正如前面的C代码所写的，login函数不会马上得到认证结果，所以执行后马上退出。另一个函数是onGetLoginResult，这个Lua函数需要当C系统中获取到认证结果后被回调执行，以真正执行login的后续操作。所以，我们还需要在C中添加回调的代码。</p>
<pre class="brush: cpp; title: ;">
// C code
// 当服务器端返回认证结果后
BOOL loginResult = ...
lua_getglobal( L, &quot;onGetLoginResult&quot; );
lua_pushboolean( L, loginResult );
lua_call( L, 1, 0 );
</pre>
<p>在这里，我们在C中hardcode了回调onGetLoginResult的代码，这很丑陋，不过可以避免，比如可以给前面注册给Lua的login函数增加一个参数callbackFunctionName，以让Lua显式的告诉系统当获取登录结果后的回调函数名称。</p>
<p>再次回到本文一开始所提出的问题，尽管在系统中的异步调用不可避免，但我们希望在Lua中能够有同步机制，即Lua脚本在得到验证结果后才允许被继续执行，如何才能做到这一点呢。</p>
<p>Lua有一个非常棒的coroutine机制，在Lua代码中可以通过协同程序来进行多线程，可以使用coroutine.yield和coroutine.resume来对协同程序进行挂起和恢复。需要达到上面所提出的目标，只需在系统中使用与coroutine.yield和coroutine.resume相对应的Lua C API——lua_yield和lua_resume即可。如下所示，login函数有些小改变：</p>
<pre class="brush: cpp; title: ;">
// C code
int login( lua_State *L )
{
	string user = luaL_checkstring( L, 1 );
	string password = luaL_checkstring( L, 2 );
	sendAuthenticationInfo( user, password );
	return lua_yield( L, 0 );
}
// 注册给Lua
lua_register( L, &quot;login&quot;, login );
</pre>
<p>可以看到，与前面的区别只有login函数的最后一句，调用了lua_yield后再返回，而不是return 0，这样就可以达到阻塞Lua脚本的目的。事实是，lua_yield是一个比较特殊的函数，它只能作为注册的C函数的返回值使用，否则调用失败。</p>
<p>当系统获得服务器端的登陆验证结果后，通过lua_resume即可恢复之前被阻塞的Lua。</p>
<pre class="brush: cpp; title: ;">
// C code
// 当服务器端返回认证结果后
BOOL loginResult = ...
lua_pushboolean( L, loginResult );
lua_resume( L, 1 );
</pre>
<p>上面的代码是当你的系统中只有一个Lua虚拟机的情形，如果使用了多个Lua虚拟机，事情稍微有一点点复杂，login函数还需要将当前调用的lua_State存储下来，以便lua_resume的时候，可以知道恢复的是哪一个被阻塞的虚拟机。</p>
<p>OK，当C中有这样的实现后，Lua程序人员将会为此而高兴，因为Lua代码变得如此简洁。</p>
<pre class="brush: jscript; title: ;">
-- Lua code
-- 当用户点击“登陆”按钮后，执行该函数
function onClickLoginBtn()
	local user = ...
	local password = ...
	if login( user, password ) == true then
		print( &quot;Authentication succeeded.&quot; )
		...omitted code...
	else
		print( &quot;Authentication failed.&quot; )
		...omitted code...
	end
end
</pre>
<p>系统or平台开发人员应当尽可能的为用户考虑，正如上面第二种解决方法所追求的那样。</p>
]]></content:encoded>
			<wfw:commentRss>http://blog.nuttycoder.com/2010/11/08/lua%e7%9b%b8%e5%85%b3%e9%97%ae%e9%a2%98%e6%95%b4%e7%90%86%ef%bc%882%ef%bc%89/feed/</wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
	</channel>
</rss>

