ngx.thread.spawn的子线程和主线程协作问题

419 views
Skip to first unread message

jason...@126.com

unread,
Jun 30, 2017, 4:33:16 AM6/30/17
to openresty
主线程ngx.thread.spawn A线程,当A线程执行到http:connect的时候会yield自己

主线程spawn完A线程后,会执行一个循环,循环结束的条件是A线程将lrucache中某一个key对应的value设置为nil。

现在遇到的问题:
    当A线程yield自己后,主线程就一直在无限循环,而A线程永远不会执行http:connect之后的代码
我已尝试的解决方案:
    1.在主线程中resume线程A,结果无效,主线程还是在无限循环
    2.在主线程中判断 
        if lrucache:get(“key”) then
            coroutine.yield(coroutine.running())
        end
       结果无效,主线程依旧死循环
求助如何解决该问题...
en-media:image/png:9b3e777feaf66d21da5c786023457636:none:none

tokers

unread,
Jun 30, 2017, 11:46:39 AM6/30/17
to openresty
比较好奇,A 线程(确切的说是 Light thread,协程)为什么不会执行 http:connect 之后的代码,执行连接操作,即使失败,也会有连接超时,那么该函数也会返回的。

jason...@126.com

unread,
Jun 30, 2017, 11:59:50 PM6/30/17
to openresty
你可以写一个简单的demo测试下,的确是这样的...我也很纳闷

在 2017年6月30日星期五 UTC+8下午11:46:39,tokers写道:

tokers

unread,
Jul 1, 2017, 3:16:58 AM7/1/17
to openresty
简单写了个 demo 测试下,并没有复现出你所说的,子协程没有执行连接操作之后的代码。
请提供一个你自己的可以复现出问题的 demo,并说明下你所使用的 openresty 版本

Zexuan Luo

unread,
Jul 1, 2017, 10:29:55 AM7/1/17
to openresty
如果我没理解错的话,现在的问题是如何中断主协程的循环,给协程A运行的机会?
试试在循环中添加 ngx.sleep,让协程A有可能重新被调度。


在 2017年6月30日星期五 UTC+8下午4:33:16,jason...@126.com写道:

FQ Liu

unread,
Jul 2, 2017, 10:34:06 PM7/2/17
to openresty
Nginx+Lua并发处理多个请求靠的是Lua协程的yield和resume。
你在主线程执行简单的死循环的话,不止子线程无法执行,整个Nginx进程都无法处理请求,因为主线程占用的100%的CPU时间。

在 2017年6月30日星期五 UTC+8下午4:33:16,jason...@126.com写道:
主线程ngx.thread.spawn A线程,当A线程执行到http:connect的时候会yield自己

tokers

unread,
Jul 2, 2017, 10:49:47 PM7/2/17
to openresty

或许你可以改下同步的方式,比如使用 ngx.thread.wait,让主协程等待子协程完成之后再继续执行后续的任务

On Friday, June 30, 2017 at 4:33:16 PM UTC+8, jason...@126.com wrote:

Zexuan Luo

unread,
Jul 3, 2017, 2:07:04 AM7/3/17
to openresty
这个想法好。
比方说用 ngx.semaphore ,直接协程A post,主协程 wait,就能解决同步问题了。不用纠结于如何在循环里让主协程调度出去的问题,而且代码还更加清爽。

在 2017年7月3日星期一 UTC+8上午10:49:47,tokers写道:

jason...@126.com

unread,
Jul 4, 2017, 8:00:31 AM7/4/17
to openresty
嗯,是这个意思,我先试下ngx.sleep这个方案,之前考虑过这个方法。担心这样会延长请求时间,所以没尝试。

在 2017年7月1日星期六 UTC+8下午10:29:55,Zexuan Luo写道:

jason...@126.com

unread,
Jul 4, 2017, 8:01:27 AM7/4/17
to openresty
是的~ 请求直接Read time out了

在 2017年7月3日星期一 UTC+8上午10:34:06,FQ Liu写道:

jason...@126.com

unread,
Jul 4, 2017, 8:03:28 AM7/4/17
to openresty
这个做不到,需要主协程和子协程轮流执行,两个携程会不停的操作同一个变量,有点类似生产者和消费者模型,但是由于代码原因,无法直接使用生产者消费者的方式实现

jason...@126.com

unread,
Jul 4, 2017, 8:07:53 AM7/4/17
to openresty
看到过这个方案,但是...这个模块现在还处于试验阶段..
This Lua module is currently considered experimental.

在 2017年7月3日星期一 UTC+8下午2:07:04,Zexuan Luo写道:

tokers

unread,
Jul 4, 2017, 10:30:33 PM7/4/17
to openresty
尝试使用 ngx.sleep(0) 吧,之前看到有个 PR 已经合并:sleep 时间为 0 时,把对应事件加入到这一轮事件的 post 队列(前提是你有使用连接锁)
这样该事件不需要等到下一轮事件调度循环时才被执行,可以减少对应这个请求的时间损耗,PR 暂时没找到~,可以去 issue 列表里找找:)

jason...@126.com

unread,
Jul 6, 2017, 3:09:44 AM7/6/17
to openresty
经过测试,问题已解决,解决主线程无限循环的方法是,ngx.sleep(0.001),使主线程sleep 1毫秒,子线程即可被调度到,另外ngx.sleep(0)是不行的...


在 2017年6月30日星期五 UTC+8下午4:33:16,jason...@126.com写道:
主线程ngx.thread.spawn A线程,当A线程执行到http:connect的时候会yield自己

tokers

unread,
Jul 6, 2017, 6:58:38 AM7/6/17
to openresty
ngx.sleep(0) 为什么不行?

jason...@126.com

unread,
Jul 9, 2017, 2:24:16 AM7/9/17
to openresty
我测试的现象试试ngx.sleep(0)是不行的,具体原因需要看源码才能知道了,需要知道ngx_lua调度线程的条件是怎样的

在 2017年7月6日星期四 UTC+8下午6:58:38,tokers写道:

tokers

unread,
Jul 9, 2017, 4:38:56 AM7/9/17
to openresty
ngx.sleep 只是把一个特殊的事件放到定时器,挂起当前协程,然后再这个事件的  handler 里 resume 被挂起的协程。

Zexuan Luo

unread,
Jul 9, 2017, 6:42:49 AM7/9/17
to openresty
当时是 Datong 的 pr 实现了通过 ngx.sleep(0) 中断事件循环的功能。 正式的发布版还没有包括这个 pr(除非用尝鲜版本)。
旧版的 ngx.sleep(0) 并不能中断事件循环。具体原因见 Datong 的解释:
https://groups.google.com/d/msg/openresty/2UXtJHvSpXM/ZyLdtKwBAAAJ
Datong 的实现是给 Nginx 打了个 patch,额外增加了新的事件队列,绕过了 Nginx 事件循环的限制。
https://github.com/openresty/openresty/blob/master/patches/nginx-1.11.2-delayed_posted_events.patch

当然就个人意见,最好的方法还是用 ngx.semaphore.wait 和 ngx.semaphore.post. 不知道你那具体的代码是怎样的,不过 semaphore 只是提供了一套信号机制,
其用法并不拘泥于生产者消费者模型。比如这个示例:
local semaphore = require "ngx.semaphore"
local sema = semaphore:new()
local function A()
    ngx
.sleep(1)
    sema
:post()
    ngx
.sleep(1)
    ngx
.update_time()
    ngx
.say("Run A at ", ngx.now())
end

-- 忽略错误处理
ngx
.thread.spawn(A)
sema
:wait(2)
ngx
.update_time()
ngx
.say("Run main at ", ngx.now())



在 2017年7月9日星期日 UTC+8下午2:24:16,jason...@126.com写道:
Reply all
Reply to author
Forward
0 new messages