libevent并不是线程安全的,但这不代表libevent不支持多线程模式。

前几天在微博上看到ruanyf发了条微博说到apache和nginx的并发模型,看到评论很多人都说不对

于是自己又查了下,总结一下我所学过的网络库或者网络服务器的并发模型

1、muduo:one loop per thread,主线程注册listen事件,通过某种负载均衡机制(round robin)将连接的事件注册到子线程的Reactor上,据说也是Netty的方案,最近也正好在学netty,刚好可以验证一下。

另外,muduo还提到了一个runInLoop()的功能:如果用户在当前线程调用,则回调functor会同步进行,如果在其他线程调用,则IO线程会被唤醒执行这个functor。这种跨线程调用是如何实现的?因为其他线程很可能阻塞在Reactor上。传统方法是用pipe(后面将提到memcache的多线程),在muduo里面是用eventfde(2),将回调放入线程的任务队列,并发送一个uint64大小的消息来唤醒Reactor。

2、nginx:master + worker的工作模式,ruanyf的微博说是master接受连接分配给worker,事实上不是这样的,master只是通过信号管理worker进程,worker之间通过accept_mutex来决定是否将监听套接字加入到loop中。所谓的one loop per process

3、libevent:这是在网上找的资料,libevent并不是线程安全,但不代表其不支持多线程。memcache的网络部分使用libevent,有一个经典的图描述了其多线程实现:
这里写图片描述

这种消息通知+同步层的机制,通过pipe和一个加锁的任务队列(CQ)实现。与muduo的eventfd效果类似。

一 线程的初始化

1线程对象

在进行事件驱动时,每个线程需建立自己的事件根基。由于libevent未提供线程之间通信的方式,我们采用管道来进行线程的通信。同时为方便主线程分配线程,我们还需保留各个线程的id号。因此我们采用如下结构来保留每个线程的有关信息。

typedef struct {

pthread_t thread_id;        #线程ID

struct event_base *base;   #事件根基

struct event notify_event;

int notify_receive_fd;

int notify_send_fd;

CQ new_conn_queue;    #连接队列

} LIBEVENT_THREAD;

2 初始化线程

我们先介绍如何为每个线程创建自己的事件根基。正如前面介绍的我们需自己建立管道来进行线程通信,因此在线程根基初始化后,我们为为管道的读添加事件对象。注意:在libevent中,线程的初始化需在事件根基初始化之后(即event_init之后)。

static void setup_thread(LIBEVENT_THREAD *me) {

if (! me->base)

me->base = event_init();

event_set(&me->notify_event, me->notify_receive_fd,

EV_READ | EV_PERSIST, thread_libevent_process, me);

event_base_set(me->base, &me->notify_event);

event_add(&me->notify_event, 0) == -1)

cq_init(&me->new_conn_queue);

}

将主线程存储于结构体的第0个对象中,然后为每个线程创建管道并创建事件根基。

threads = malloc(sizeof(LIBEVENT_THREAD) * nthreads);

threads[0].base = main_base;

threads[0].thread_id = pthread_self();

for (i = 0; i < nthreads; i++) {

int fds[2];

pipe(fds)

threads[i].notify_receive_fd = fds[0];

threads[i].notify_send_fd = fds[1];

setup_thread(&threads[i]);

接下来从主线程中创建线程,线程创建成功后激活该线程的事件根基,进入事件循环。

for (i = 1; i < nthreads; i++) {

pthread_t thread;

pthread_attr_t attr;

pthread_attr_init(&attr);

pthread_create(&thread, &attr, worker_libevent, &threads[i])

}

/* 激活事件根基.*/

static void *worker_libevent(void *arg) {

LIBEVENT_THREAD *me = arg;

return (void*) event_base_loop(me->base, 0);

}

二 分发连接

现在我们有了多个线程和事件根基,那么我们应该如何将主线程接收到的连接分发给各个线程并将其激活呢?本例子中线程的选择采用最简单的轮询方式。

我们需要对accept_handle函数进行修改。在线程池模型中,我们使用子线程代替主线程来创建缓冲区事件对象,主线程只是对激活选中的线程。我们通过向管道写入一个空字节来激活该管道的读请求。

thread += 1;

item->sfd = sfd

cq_push(&threads[thread].new_conn_queue, item);

write(threads[thread].notify_send_fd, "", 1)

三 处理请求

管道的读请求就绪后,回调函数thread_libevent_process被调用,此时就进入到了线程中,后面所有的调度都将在该线程中进行。

首先从管道中读取1个字节,然后创建事件缓冲区对象来实际处理请求。

static void thread_libevent_process(int fd, short which, void *arg) {

char buf[1];

read(fd, buf, 1)

}

四结束语

到这里我们的程序就可以使用线程池和libevent事件驱动来协同工作了。在实际的服务中,我们通常要将服务作为守护进程来运行,那么在linux中,如何编写守护进程呢?后面在继续学习如何编写linux守护进程。

libevent实现多线程的更多相关文章

  1. Libevent 的多线程操作

    起因是event_base 跨线程add/remove event 导致崩溃或者死循环. 据查:libvent 1.4.x是非线程安全的,要跨线程执行event_add,会有问题.因此传统做法是通过p ...

  2. 使用libevent进行多线程socket编程demo

    最近要对一个用libevent写的C/C++项目进行修改,要改成多线程的,故做了一些学习和研究. libevent是一个用C语言写的开源的一个库.它对socket编程里的epoll/select等功能 ...

  3. memcached使用libevent 和 多线程模式

    一.libevent的使用 首先我们知道,memcached是使用了iblievet作为网络框架的,而iblievet又是单线程模型的基于linux下epoll事件的异步模型.因此,其基本的思想就是 ...

  4. 基于Libevent的HTTP Server

    简单的Http Server 使用Libevent内置的http相关接口,可以很容易的构建一个Http Server,一个简单的Http Server如下: #include <event2/e ...

  5. libevent源码深度剖析

    原文地址: http://blog.csdn.net/sparkliang/article/details/4957667 第一章 1,前言 Libevent是一个轻量级的开源高性能网络库,使用者众多 ...

  6. libevent源码剖析

    libevent是一个使用C语言编写的,轻量级的开源高性能网络库,使用者很多,研究者也很多.由于代码简洁,设计思想简明巧妙,因此很适合用来学习,提升自己C语言的能力. libevent有这样显著地几个 ...

  7. libevent学习一

    常见的异步IO存在的问题:   1.使用 fcntl(fd, F_SETFL, O_NONBLOCK);,为什么在处理上效率不好.       a.在没有数据可读写的时候,循环会不停执行,浪费掉大部分 ...

  8. libevent 源码深度剖析十三

    libevent 源码深度剖析十三 —— libevent 信号处理注意点 前面讲到了 libevent 实现多线程的方法,然而在多线程的环境中注册信号事件,还是有一些情况需要小心处理,那就是不能在多 ...

  9. libevent源码深度剖析十二

    libevent源码深度剖析十二 ——让libevent支持多线程 张亮 Libevent本身不是多线程安全的,在多核的时代,如何能充分利用CPU的能力呢,这一节来说说如何在多线程环境中使用libev ...

随机推荐

  1. P7416 [USACO21FEB] No Time to Dry P

    题目传送门 题意简述:给出颜色序列 \(a\),多次询问给出 \(l,r\),求涂成 \(a_l,a_{l+1},\cdots,a_r\) 的最小操作次数.每次涂色只能用一段数值更大的颜色覆盖原有的颜 ...

  2. DirectX12 3D 游戏开发与实战第五章内容

    渲染流水线 学习目标: 了解用于在2D图像中表现出场景立体感和空间深度感等真实效果的关键因素 探索如何用Direct3D表示3D对象 学习如何建立虚拟摄像机 理解渲染流水线,根据给定的3D场景的几何描 ...

  3. 使用mamba加快conda安装软件速度?

    conda是个安装软件的神器,但镜像不稳定,下载安装软件的速度有时很慢.对于几十Mb甚至上百Mb的软件往往下不动,下了半天可能失败. 找了一个叫mamba的加速神器,可以用来并行下载和安装,大大加快速 ...

  4. mGWAS研究思路

    mGWAS(metabolome-based genome-wide association studies)的研究方法和GWAS类似,只是将代谢物含量作为表型性状来与基因位点进行关联.但同时它也有自 ...

  5. 前端1 — HTML — 更新完毕

    1.首先来了解一个东西 -- W3C标准( 全称是:World Wide Web Consortium ) 万维网联盟(外语缩写:W3C)标准不是某一个标准,而是一系列标准的集合 -- 这个其实每天都 ...

  6. 【STM8】STM8S介绍(编程环境、烧录、芯片内容)(Vcap需要一个电容接地)

    这篇博客的介绍大纲 [1]我使用的开发板和烧录器 [2]编程环境 [3]烧录软件和界面 [4]芯片内容 [1]我使用的开发板和烧录器 首先,我用的是STM8S003F3P6这款开发板,淘宝上就有了,5 ...

  7. Virtual Destructor

    Deleting a derived class object using a pointer to a base class that has a non-virtual destructor re ...

  8. linux-源码软件管理-yum配置

    总结如下:1.源码配置软件管理2.配置yum本地源和网络源及yum 工作原理讲解3.计算机硬盘介绍 1.1 源码管理软件 压缩包管理命令: # 主流的压缩格式包括tar.rar.zip.war.gzi ...

  9. Centos 7 安装redis,修改配置文件不生效、外网不能访问。

    前提: 在用Centos 7 安装 redis 时,遇上一下几个问题 ,记录下 . 1.修改配置文件,按官网步骤启动,不生效. 2.外网无法访问redis. 步骤: 1.打开centos 虚拟机 ,按 ...

  10. spring Profile 为不同环境提供不同的配置支持

    说明 Profile为在不同环境下使用不同的配置提供了支持(开发环境下的配置和生产环境下的配置肯定是不同的, 例如, 数据库的配置) . 在spring开发中用@Profile 注解使用来选择行配置系 ...