首先简单介绍下distri.lua中的线程设计方案.

distri.lua提供一个API函数fork用于创建新的C线程,这个C线程运行独立的lua虚拟机,为了在各线程之间通信

每个线程都会创建一个channel,用于接收其它线程发送过来的消息.

这个channel内部实现为单向链表,为了将channel的处理与网络消息处理接口合并,channel使用tls为每个单

独的 线程创建一个管道,这个管道被添加到proactor中监听,如果一个线程尝试从channel中读消息,而消息队

列为空,就 将这个管道添加到channel的等待列表中.其它线程向channel添加消息后会检查等待列表,如果等

待列表中有元素 则弹出一个,并向弹出的管道中写入一个字节以通知接收者有消息可读.向管道中写入数据之

后,接收线程的proactor 发现一个管道被激活,事件处理循环触发从那个管道对应的channel中继续读取消息.

下面分析下关键代码:

struct channel_pth{
kn_fd base;
kn_dlist_node node;
int notifyfd;
kn_list local_que;
kn_channel_t channel;
};

每个期望从一个channel对象中接收消息的线程都会产生一个对应的struct channel_pth,struct channel_pth

继承自kn_fd,也就是说struct channel_pth可以被添加到cproctor中监听.notifyfd则用于其它线程发送通知消息,

以通知管道中有新消息到来.

int kn_channel_bind(struct kn_proactor *p,kn_channel_t c){
struct channel_pth *pth = (struct channel_pth*)pthread_getspecific(c->t_key);
if(pth) return -;
pth = calloc(,sizeof(*pth)); pth->base.type = CHANNEL;
int tmp[];
if(pipe(tmp) != ){
free(pth);
return -;
}
pth->base.fd = tmp[];
pth->notifyfd = tmp[];
pth->base.on_active = kn_channel_on_active;
pth->base.process = kn_channel_process;
pth->channel = c;
fcntl(tmp[], F_SETFL, O_NONBLOCK | O_RDWR);
fcntl(tmp[], F_SETFL, O_NONBLOCK | O_RDWR);
kn_ref_init(&pth->base.ref,channel_pth_destroy);
if(!= p->Register(p,(kn_fd_t)pth)){
kn_ref_release((kn_ref*)pth);
return -;
}
kn_ref_acquire(&c->ref);
pthread_setspecific(c->t_key,(void*)pth);
kn_procator_putin_active(p,(kn_fd_t)pth);
return ;
}

用于将一个channel绑定到proactor,可以看到,在绑定时首先为当前线程产生一个struct channel_pth 对象,

并正始化相关的管道.然后将管道的读端添加到proactor中.要注意的是管道的监听模式也是edge trigger. 绑定完成

之后将这个struct channel_pth添加到激活队列中,这样在proactor的主循环中将不断的从对应的 channel中提

取消息,之后消息为空,才从活队列移除.

static int8_t kn_channel_process(kn_fd_t s){
struct channel_pth* c = (struct channel_pth*)s;
struct msg *msg;
int n = ;
while((msg = kn_channel_getmsg(c)) != NULL && n > ){
c->channel->cb_msg(c->channel,msg->sender,msg->data);
free(msg->data);
free(msg);
--n;
}
if(n <= )
return ;
else
return ;
}

proactor主循环中对每个 struct channel_pth的处理,不断的尝试从channel中提取消息, 调用回调函数处理消息.

这里要注意其中的一个条件值1024.这个值的设定是为了防止一个channel 中消息过多,cpu时间全都被用于处理这个

channel,网络事件和其它的channel都每机会执行.所以, 对每个channel在一次处理中最多只提取1024个消息,如果还

有剩余到下一个循环再继续处理.

static inline struct msg* kn_channel_getmsg(struct channel_pth *c){
struct msg *msg = (struct msg*)kn_list_pop(&c->local_que);
if(msg) return msg;
kn_mutex_lock(c->channel->mtx);
if(!kn_list_size(&c->channel->queue)){
kn_dlist_push(&c->channel->waits,&c->node);
kn_mutex_unlock(c->channel->mtx);
return NULL;
}else{
kn_list_swap(&c->local_que,&c->channel->queue);
}
kn_mutex_unlock(c->channel->mtx);
return (struct msg*)kn_list_pop(&c->local_que);
}

从channel中提取消息,如果channel为空将当前channel对应的struct channel_pth添加 到等待队列中.

void kn_channel_putmsg(kn_channel_t to,kn_channel_t from,void *data)
{
kn_dlist_node *tmp = NULL;
int ret = ;
struct msg *msg = calloc(,sizeof(*msg));
msg->sender = from;
msg->data = data;
kn_mutex_lock(to->mtx);
kn_list_pushback(&to->queue,&msg->node);
while(){
tmp = kn_dlist_first(&to->waits);
if(tmp){
//有线程在等待消息,通知它有消息到了
struct channel_pth *pth = (struct channel_pth*)(((char*)tmp)-sizeof(kn_fd));
ret = write(pth->notifyfd,"",);
kn_dlist_pop(&to->waits);
if(!(ret == || (ret < && errno != EAGAIN)))
break;
/*if(ret == 0 || (ret < 0 && errno != EAGAIN)){
//对端关闭
kn_dlist_pop(&to->waits);
}else
break;*/
}else
break;
};
kn_mutex_unlock(to->mtx);
}

向channel投递消息,首先将消息写入到channel中,然后查看等待列表看看是否有线正在等待消息,如果有

则从等待列表中弹出一个等待者,并向那个等待者对应的管道写入一个字节以通知channel有消息到来.

distri.lua线程间通信的设计的更多相关文章

  1. Java线程间通信-回调的实现方式

    Java线程间通信-回调的实现方式   Java线程间通信是非常复杂的问题的.线程间通信问题本质上是如何将与线程相关的变量或者对象传递给别的线程,从而实现交互.   比如举一个简单例子,有一个多线程的 ...

  2. Android线程间通信机制——深入理解 Looper、Handler、Message

    在Android中,经常使用Handler来实现线程间通信,必然要理解Looper , Handler , Message和MessageQueue的使用和原理,下面说一下Looper , Handl ...

  3. Java多线程:线程间通信之Lock

    Java 5 之后,Java在内置关键字sychronized的基础上又增加了一个新的处理锁的方式,Lock类. 由于在Java线程间通信:volatile与sychronized中,我们已经详细的了 ...

  4. Android中线程间通信原理分析:Looper,MessageQueue,Handler

    自问自答的两个问题 在我们去讨论Handler,Looper,MessageQueue的关系之前,我们需要先问两个问题: 1.这一套东西搞出来是为了解决什么问题呢? 2.如果让我们来解决这个问题该怎么 ...

  5. 说说Java线程间通信

    序言 正文 [一] Java线程间如何通信? 线程间通信的目标是使线程间能够互相发送信号,包括如下几种方式: 1.通过共享对象通信 线程间发送信号的一个简单方式是在共享对象的变量里设置信号值:线程A在 ...

  6. 说说 Java 线程间通信

    序言 正文 一.Java线程间如何通信? 线程间通信的目标是使线程间能够互相发送信号,包括如下几种方式: 1.通过共享对象通信 线程间发送信号的一个简单方式是在共享对象的变量里设置信号值:线程A在一个 ...

  7. 0038 Java学习笔记-多线程-传统线程间通信、Condition、阻塞队列、《疯狂Java讲义 第三版》进程间通信示例代码存在的一个问题

    调用同步锁的wait().notify().notifyAll()进行线程通信 看这个经典的存取款问题,要求两个线程存款,两个线程取款,账户里有余额的时候只能取款,没余额的时候只能存款,存取款金额相同 ...

  8. 线程间通信 GET POST

    线程间通信有三种方法:NSThread   GCD  NSOperation       进程:操作系统里面每一个app就是一个进程. 一个进程里面可以包含多个线程,并且我们每一个app里面有且仅有一 ...

  9. Java多线程编程核心技术---线程间通信(二)

    通过管道进行线程间通信:字节流 Java提供了各种各样的输入/输出流Stream可以很方便地对数据进行操作,其中管道流(pipeStream)是一种特殊的流,用于在不同线程间直接传送数据,一个线程发送 ...

随机推荐

  1. git代码合并:Merge、Rebase的选择

    代码合并:Merge.Rebase的选择 Zhongyi Tong edited this page on Dec 7, 2015 · 3 revisions Pages 19 Home 2.1 快速 ...

  2. 解决IE9下交通银行网上银行无法输入密码的问题

    自系统升级到 Win 7以后,突然发现用 IE9 浏览器登陆交通银行网上银行时,始终不能正常 输入密码.原来,非要进行特别的设置才可.现记录如下: 1.没有交通银行安装安全输入控件,安装即可.   当 ...

  3. android:ProgressDialog控件

    ProgressDialog 和 AlertDialog 有点类似,都可以在界面上弹出一个对话框,都能够屏蔽 掉其他控件的交互能力.不同的是,ProgressDialog 会在对话框中显示一个进度条, ...

  4. 第五章703N 刷openwrt 挂载u盘

    感谢原文 http://blog.csdn.net/qinpeng_zbdx/article/details/8577212 http://arch.pconline.com.cn//pcjob/sy ...

  5. 异常 try catch finally return 执行关系 MD

    Markdown版本笔记 我的GitHub首页 我的博客 我的微信 我的邮箱 MyAndroidBlogs baiqiantao baiqiantao bqt20094 baiqiantao@sina ...

  6. linux系统下创建oracle表空间和用户权限查询

    创建用户和表空间: 1.登录linux,以oracle用户登录(如果是root用户登录的,登录后用 su - oracle命令切换成oracle用户) 2.以sysdba方式来打开sqlplus,命令 ...

  7. Oracle 12c RMAN备份文档

    创建备份目录,查看剩余空间 [cistest@cistest ~]$ df -h df: '/home/oratest/.gvfs': Permission denied Filesystem Siz ...

  8. Eclipse 个人使用配置

    个人最喜欢使用的是eclipse,但是每次有新的版本或者是在不同的电脑上都要一遍一遍的配置.下面收集自己每次用eclipse需要注意配置的地方: 快捷键只需要修改一个code assitant 修改显 ...

  9. tar 的–exclude参数,实现不包括某些文件(转)

    最近有人问我tar压缩能不能不包含不想要的文件或者文件夹 压缩包很利于传送,备份等等.但比如大量的日志文件一般是不需要备份,或者需要分开备份. tar参数中的–exclude,这个是我在看鸟哥私房菜时 ...

  10. shell变量的截取总结

    #shell变量的截取 Shell中的${}.##和%% 假设定义了一个变量为: 代码如下: file=/dir1/dir2/dir3/my.file.txt //变量的删除 可以用${ }分别替换得 ...