网络编程实战
盛延敏
前大众点评云平台首席架构师
立即订阅
6034 人已学习
课程目录
已完结 39 讲
0/4登录后,你可以任选4讲全文学习。
开篇词 (1讲)
开篇词 | 学好网络编程,需要掌握哪些核心问题?
免费
第一模块:基础篇 (9讲)
01 | 追古溯源:TCP/IP和Linux是如何改变世界的?
02 | 网络编程模型:认识客户端-服务器网络模型的基本概念
03丨套接字和地址:像电话和电话号码一样理解它们
04 | TCP三次握手:怎么使用套接字格式建立连接?
05 | 使用套接字进行读写:开始交流吧
06 | 嗨,别忘了UDP这个小兄弟
07 | What? 还有本地套接字?
08 | 工欲善其事必先利其器:学会使用各种工具
09丨答疑篇:学习网络编程前,需要准备哪些东西?
第二模块:提高篇 (10讲)
10 | TIME_WAIT:隐藏在细节下的魔鬼
11 | 优雅地关闭还是粗暴地关闭 ?
12 | 连接无效:使用Keep-Alive还是应用心跳来检测?
13 | 小数据包应对之策:理解TCP协议中的动态数据传输
14丨UDP也可以是“已连接”?
15 | 怎么老是出现“地址已经被使用”?
16 | 如何理解TCP的“流”?
17 | TCP并不总是“可靠”的?
18 | 防人之心不可无:检查数据的有效性
19丨提高篇答疑:如何理解TCP四次挥手?
期中复习周 (2讲)
期中大作业丨动手编写一个自己的程序吧!
免费
期中大作业丨题目以及解答剖析
免费
第三模块:性能篇 (12讲)
20 | 大名⿍⿍的select:看我如何同时感知多个I/O事件
21 | poll:另一种I/O多路复用
22 | 非阻塞I/O:提升性能的加速器
23 | Linux利器:epoll的前世今生
24 | C10K问题:高并发模型设计
25 | 使用阻塞I/O和进程模型:最传统的方式
26 | 使用阻塞I/O和线程模型:换一种轻量的方式
27 | I/O多路复用遇上线程:使用poll单线程处理所有I/O事件
28 | I/O多路复用进阶:子线程使用poll处理连接I/O事件
29 | 渐入佳境:使用epoll和多线程模型
30 | 真正的大杀器:异步I/O探索
31丨性能篇答疑:epoll源码深度剖析
第四模块:实战篇 (4讲)
32 | 自己动手写高性能HTTP服务器(一):设计和思路
33 | 自己动手写高性能HTTP服务器(二):I/O模型和多线程模型实现
34 | 自己动手写高性能HTTP服务器(三):TCP字节流处理和HTTP协议实现
35 | 答疑:编写高性能网络编程框架时,都需要注意哪些问题?
结束语 (1讲)
结束语丨我相信这不是结束,让我们江湖再见
网络编程实战
登录|注册

33 | 自己动手写高性能HTTP服务器(二):I/O模型和多线程模型实现

盛延敏 2019-10-23
你好,我是盛延敏,这里是网络编程实战第 33 讲,欢迎回来。
这一讲,我们延续第 32 讲的话题,继续解析高性能网络编程框架的 I/O 模型和多线程模型设计部分。

多线程设计的几个考虑

在我们的设计中,main reactor 线程是一个 acceptor 线程,这个线程一旦创建,会以 event_loop 形式阻塞在 event_dispatcher 的 dispatch 方法上,实际上,它在等待监听套接字上的事件发生,也就是已完成的连接,一旦有连接完成,就会创建出连接对象 tcp_connection,以及 channel 对象等。
当用户期望使用多个 sub-reactor 子线程时,主线程会创建多个子线程,每个子线程在创建之后,按照主线程指定的启动函数立即运行,并进行初始化。随之而来的问题是,主线程如何判断子线程已经完成初始化并启动,继续执行下去呢?这是一个需要解决的重点问题。
在设置了多个线程的情况下,需要将新创建的已连接套接字对应的读写事件交给一个 sub-reactor 线程处理。所以,这里从 thread_pool 中取出一个线程,通知这个线程有新的事件加入。而这个线程很可能是处于事件分发的阻塞调用之中,如何协调主线程数据写入给子线程,这是另一个需要解决的重点问题。
取消
完成
0/1000字
划线
笔记
复制
© 版权归极客邦科技所有,未经许可不得传播售卖。 页面已增加防盗追踪,如有侵权极客邦将依法追究其法律责任。
该试读文章来自付费专栏《网络编程实战》,如需阅读全部文章,
请订阅文章所属专栏。
立即订阅
登录 后留言

精选留言(5)

  • 小蚂蚁
    老师,你好,有个地方不是很明白,
    为什么event_loop_channel_buffer_nolock(eventLoop, fd, channel1, type);是往子线程的数据中增加需要处理的 channel event 对象呢?

    void event_loop_channel_buffer_nolock(struct event_loop *eventLoop, int fd, struct channel *channel1, int type) {
        //add channel into the pending list
        struct channel_element *channelElement = malloc(sizeof(struct channel_element));
        channelElement->channel = channel1;
        channelElement->type = type;//1 add (1: add 2: delete)
        channelElement->next = NULL;
        //第一个元素 channel_element是channel的链表,
        // eventLoop pending_head和pending_tail维护的是channelElement的链表
        //这样的话最终还是event_loop包含了channel(event_loop->channelElement->channel)
        if (eventLoop->pending_head == NULL) {
            eventLoop->pending_head = eventLoop->pending_tail = channelElement;
        } else {
            eventLoop->pending_tail->next = channelElement;
            eventLoop->pending_tail = channelElement;
        }
    }


    void *event_loop_thread_run(void *arg) {
        struct event_loop_thread *eventLoopThread = (struct event_loop_thread *) arg;

        pthread_mutex_lock(&eventLoopThread->mutex);

        // 初始化化event loop,之后通知主线程
        eventLoopThread->eventLoop = event_loop_init_with_name(eventLoopThread->thread_name);
        yolanda_msgx("event loop thread init and signal, %s", eventLoopThread->thread_name);
        pthread_cond_signal(&eventLoopThread->cond);

        pthread_mutex_unlock(&eventLoopThread->mutex);

        //子线程event loop run
        event_loop_run(eventLoopThread->eventLoop);
    }
    struct event_loop_thread {
        struct event_loop *eventLoop;//主线程和子线程共享
        pthread_t thread_tid; /* thread ID */
        pthread_mutex_t mutex;
        pthread_cond_t cond;
        char * thread_name;
        long thread_count; /* # connections handled */
    };


    event_loop_channel_buffer_nolock这个函数中是往eventLoop的链表中注册事件,可是这里的eventLoop是和子线程处理函数
    event_loop_thread_run中eventLoopThread->eventLoop不是一个eventLoop啊,这个eventLoopThread->eventLoop不才是主子线程共享的吗?
    2019-12-11
  • 鱼向北游
    回老师上一条 把代码netty贴过来了
         private static final class PowerOfTwoEventExecutorChooser implements EventExecutorChooser {
            private final AtomicInteger idx = new AtomicInteger();
            private final EventExecutor[] executors;

            PowerOfTwoEventExecutorChooser(EventExecutor[] executors) {
                this.executors = executors;
            }

            @Override
            public EventExecutor next() {
                return executors[idx.getAndIncrement() & executors.length - 1];
            }
        }

        private static final class GenericEventExecutorChooser implements EventExecutorChooser {
            private final AtomicInteger idx = new AtomicInteger();
            private final EventExecutor[] executors;

            GenericEventExecutorChooser(EventExecutor[] executors) {
                this.executors = executors;
            }

            @Override
            public EventExecutor next() {
                return executors[Math.abs(idx.getAndIncrement() % executors.length)];
            }
        }

    作者回复: 感觉还是按照顺序在取线程号啊。

    2019-10-29
    1
  • MoonGod
    老师关于加锁这里有个疑问,如果加锁的目的是让主线程等待子线程初始化event loop。那不加锁不是也可以达到这个目的吗?主线程while 循环里面不断判断子线程的event loop是否不为null不就可以了?为啥一定要加一把锁呢?

    作者回复: 好问题, 我答疑统一回答吧。

    2019-10-23
    1
  • 鱼向北游
    netty选子线程是两种算法,都是有个原子自增计数,如果线程数不是2的幂用取模,如果是就是按位与线程数减一

    作者回复: 嗯,涨知识了,代码贴一个?

    2019-10-23
  • 程序水果宝
    求完整的代码链接

    编辑回复: 代码链接请去详情页查看。

    2019-10-23
收起评论
5
返回
顶部