<转>多线程中的lua同步问题

简介: 转自 http://www.cnblogs.com/ghost240/p/3526185.html 最近写paintsnow::start时出现了一个非常麻烦的BUG,程序的Release版本大约每运行十几次就会有一次启动时崩溃(Debug版本还没崩溃过),崩溃点也不固定。

转自 http://www.cnblogs.com/ghost240/p/3526185.html

最近写paintsnow::start时出现了一个非常麻烦的BUG,程序的Release版本大约每运行十几次就会有一次启动时崩溃(Debug版本还没崩溃过),崩溃点也不固定。经过简单分析之后,确定是线程同步的问题。于是便修改了线程通信的代码,并使用pthread_mutex_lock/unlock来防止冲突。重新编译后,崩溃频率有所减少。但是每运行约四十次,还是免不了崩溃一次,而且冷启动时崩溃概率更大。

在VC中的Release版本中设置Generate Debug Info后,重复多次运行程序,好不容易得到了几次崩溃记录。综合这几次的崩溃点,可以发现崩溃基本都发生在重分配内存(operator new())时,而且调用堆栈中还显示调用过程中有luaVM的参与。经过多日跟踪,终于发现问题所在,特作记录。

首先简单说一下paintsnow的组织结构。paintsnow::start主要是由两个线程并行执行的,其中一个负责构架、绘图、消息处理等,由底层核心paintsnow.lib提供,另一个执行lua脚本,由luaVM和一些Paintsnow API构成。由于两个线程有时候需要调用另一个线程所提供的功能,因此可能会出现两个线程冲突的问题。之前的代码仅仅是对脚本线程call核心线程时做过调用转接和加锁处理,但是实际上有时核心线程也会call脚本(通常出现在各种回调函数中)。

网上的资料说解决多线程调用同一个luaVM的有效方案是使用lua_newthread来创建一个脚本线程单元,然后用新创建的单元作为lua_State来call。这一招在通常情况下是非常管用的,因为它成功避免了lua栈上的冲突。painsnow以前的方案就是这样写的。可是问题就出在lua本身为了兼容标准,并不包含线程同步的处理函数,所谓的newthread只是逻辑上的thread而非操作系统概念的thread,因此这样调用luaVM依然存在着重入的风险,特别是在内存重分配时会产生意思不到的错误。

当然,对于lua这种设计得非常优雅的库,自然对多线程有过考虑。通过lua的源码可以看出,lua在每一个不可重入的函数中都用lua_lock创建了一个锁以保证同步。默认情况下,由于同步锁并不包含在标准库中,所以lua_lock实际只是一个空宏:(llmits.h中有定义)

#define lua_lock(L) ((void)0)

如果想要添加多线程支持,需要重写它和lua_unlock两个宏。这不是难事,以添加pthread提供锁功能为例:

在lstate.h中,对GlobalState结构新加一个成员:

pthread_mutex_t lock;

然后在lua_newstate中添加初始化代码:

pthread_mutex_init(&g->lock, NULL);

接着重定义lock/unlock宏(写在原定义前面即可):

#define lua_lock(L) pthread_mutex_lock(&(G(L)->lock));

#define lua_unlock(L) pthread_mutex_unlock(&(G(L)->lock));

最后在close_state函数的末尾添加两行:


static void close_state (lua_State *L) {
  global_State *g = G(L);
  luaF_close(L, L->stack);  /* close all upvalues for this thread */
  luaC_freeallobjects(L);  /* collect all objects */
  luaM_freearray(L, G(L)->strt.hash, G(L)->strt.size);
  luaZ_freebuffer(L, &g->buff);
  freestack(L);
  lua_assert(gettotalbytes(g) == sizeof(LG));
 pthread_mutex_unlock(&g->lock);
 pthread_mutex_destroy(&g->lock);

  (*g->frealloc)(g->ud, fromstate(L), sizeof(LG), 0);  /* free main block */
}

 

 

vc++ lua线程同步代源码修改 见 http://www.cnblogs.com/zhangdongsheng/p/3679024.html

相关文章
|
12月前
|
编译器 Linux 调度
|
3月前
|
NoSQL Redis
Redis 执行 Lua保证原子性原理
Redis 执行 Lua 保证原子性原理
292 1
|
2月前
|
NoSQL 网络协议 Unix
1)Redis 属于单线程还是多线程?不同版本之间有什么区别?
1)Redis 属于单线程还是多线程?不同版本之间有什么区别?
59 1
|
6月前
|
数据采集 调度 计算机视觉
3段代码详解python中的单线程、多线程和多进程
3段代码详解python中的单线程、多线程和多进程
100 0
|
12月前
|
存储 安全 编译器
|
消息中间件 NoSQL Redis
深入探究Redis事务和Lua脚本:实现原子操作与复杂业务逻辑
本篇深入剖析了Redis的事务处理和Lua脚本特性,为读者呈现了如何利用这两个功能来实现数据的原子操作和执行复杂的业务逻辑。我们首先介绍了Redis事务的概念和基本操作,通过MULTI、EXEC、DISCARD和WATCH等命令,展示了如何在一组命令中保持原子性。进一步,我们探讨了事务命令的使用方法,演示了如何在事务中监视键变化以及提交事务。
771 0
|
缓存 调度 芯片
同步多线程
<div id="card-container" class="mod-top" style="clear:both"> <div class="card-summary nslog-area clearfix"> <div class="card-summary-content"> <div class="para">同步多线程(SMT)是一种在一个CPU 的<a target="
1701 0
|
Java 微服务
REDIS09_分布式锁的概述、加锁使用sexnu、解锁使用lua脚本保证原子性、引发的问题思考(四)
REDIS09_分布式锁的概述、加锁使用sexnu、解锁使用lua脚本保证原子性、引发的问题思考(四)
199 0
REDIS09_分布式锁的概述、加锁使用sexnu、解锁使用lua脚本保证原子性、引发的问题思考(四)
|
NoSQL Java Redis
REDIS09_分布式锁的概述、加锁使用sexnu、解锁使用lua脚本保证原子性、引发的问题思考(一)
REDIS09_分布式锁的概述、加锁使用sexnu、解锁使用lua脚本保证原子性、引发的问题思考(一)
139 0
REDIS09_分布式锁的概述、加锁使用sexnu、解锁使用lua脚本保证原子性、引发的问题思考(一)
|
NoSQL 应用服务中间件 Redis
REDIS09_分布式锁的概述、加锁使用sexnu、解锁使用lua脚本保证原子性、引发的问题思考(五)
REDIS09_分布式锁的概述、加锁使用sexnu、解锁使用lua脚本保证原子性、引发的问题思考(五)
137 0
REDIS09_分布式锁的概述、加锁使用sexnu、解锁使用lua脚本保证原子性、引发的问题思考(五)