TinyWebServer icon indicating copy to clipboard operation
TinyWebServer copied to clipboard

reactor模式的效率比模拟preactor模式的效率低吗?

Open YuXiang-ZhuanSun opened this issue 3 years ago • 20 comments

在reator模式中,IO读写是多线程并行的, 但是模拟preactor模式中,IO读写都是在主线程中串行完成的, 按道理说,并行reactor会更快才对。

但是从参考的运行结果看,reactor并没有明显的性能优势,这是为什么呢? 是收到了测试环境的影响吗? 还是IO读写在整个过程中占比很小,并不是耗时的大头?

YuXiang-ZhuanSun avatar Sep 07 '21 07:09 YuXiang-ZhuanSun

同问

makeuptransfer avatar Sep 07 '21 16:09 makeuptransfer

同问

fwaytoday avatar Sep 11 '21 02:09 fwaytoday

同问

duanchao1019 avatar Oct 05 '21 12:10 duanchao1019

我觉得reactor模式代码有问题。代码中将读事件插入阻塞队列中后有一个while(true)循环一直等到线程从阻塞队列中取此http链接后接受数据后才break。相当于阻塞队列中永远至多有一个http连接。

lxl999 avatar Nov 10 '21 14:11 lxl999

我觉得reactor模式代码有问题。代码中将读事件插入阻塞队列中后有一个while(true)循环一直等到线程从阻塞队列中取此http链接后接受数据后才break。相当于阻塞队列中永远至多有一个http连接。

同样的看法,感觉这样写的确是会把主线程阻塞在ebServer::dealWithRead这里了,完全没有体现出线程池的作用。

YYYYWD avatar Dec 27 '21 06:12 YYYYWD

我觉得reactor模式代码有问题。代码中将读事件插入阻塞队列中后有一个while(true)循环一直等到线程从阻塞队列中取此http链接后接受数据后才break。相当于阻塞队列中永远至多有一个http连接。

请教老哥觉得怎么修改才合理,能不能直接删掉这个while呢

tomatowithpotato avatar May 08 '22 02:05 tomatowithpotato

真心提问:为什么理论上说模拟的proactor会比reactor慢呢,模拟proactor中主线程是异步线程,按理说应该至少不会比多个reactor并行慢呀。 等个大佬解释一下。

wqy296802789 avatar May 19 '22 06:05 wqy296802789

真心提问:为什么理论上说模拟的proactor会比reactor慢呢,模拟proactor中主线程是异步线程,按理说应该至少不会比多个reactor并行慢呀。 等个大佬解释一下。

本人也不大懂,我的理解是因为proactor里IO任务都交给主线程串行执行,而IO任务一般是非常慢的,这样的话新的连接到来了也没法被及时处理

tomatowithpotato avatar May 19 '22 07:05 tomatowithpotato

我觉得reactor模式代码有问题。代码中将读事件插入阻塞队列中后有一个while(true)循环一直等到线程从阻塞队列中取此http链接后接受数据后才break。相当于阻塞队列中永远至多有一个http连接。

请教老哥觉得怎么修改才合理,能不能直接删掉这个while呢

我直接删掉了,结果网页可以正常运行,但是webbench压测的Requests一直为0,pages/min也为0,但是bytes/sec正常,不知道什么原因

JackMariano avatar Jun 16 '22 14:06 JackMariano

找到原因了,注释掉读事件的循环之后工作线程会吧improv置为1,但是主线程的循环被注释后会导致iimprov绝大部分事件为0,之后在读事件之后才能为1。这就导致写事件很可能在工作线程没有写完之前就跳出循环了,没有结束连接,又因为webbench的模拟的请求都是短连接,在写事件完成应该关闭连接才能够算是一次事件,这样就造成压测的结果异常。

JackMariano avatar Jun 17 '22 14:06 JackMariano

想要解决这个问题,应该把主线程判断工作线程io操作的这个逻辑让工作线程自己完成。

JackMariano avatar Jun 17 '22 14:06 JackMariano

找到原因了,注释掉读事件的循环之后工作线程会吧improv置为1,但是主线程的循环被注释后会导致iimprov绝大部分事件为0,之后在读事件之后才能为1。这就导致写事件很可能在工作线程没有写完之前就跳出循环了,没有结束连接,又因为webbench的模拟的请求都是短连接,在写事件完成应该关闭连接才能够算是一次事件,这样就造成压测的结果异常。

good work!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

tomatowithpotato avatar Jun 22 '22 02:06 tomatowithpotato

我觉得reactor模式代码有问题。代码中将读事件插入阻塞队列中后有一个while(true)循环一直等到线程从阻塞队列中取此http链接后接受数据后才break。相当于阻塞队列中永远至多有一个http连接。

请教老哥觉得怎么修改才合理,能不能直接删掉这个while呢 我觉得修改的话需要把原本主线程执行的逻辑挪到线程池模块,比如关闭连接和删除定时器。但是这里的Webserver类是包含线程池的头文件的,如果threadpool中的工作线程需要调这个定时器删除逻辑的话,应该引入Webserver的头文件,然后把Webserver对象传入。但是这样会造成头文件循环依赖相互包含,我想的是把Webserver类维护定时器的职能拆分出来独立为一个模块,然后Webserver和threadpool同时包含他的实例化对象,让工作线程读完之后自己判断是否删除定时器以及关闭连接。主线程只负责监听事件即可。

JackMariano avatar Jun 22 '22 03:06 JackMariano

我觉得这个reacotor之所以这样写很大可能是因为作者偷懒了,先写的模拟proactor。在这个框架上写reactor,所以职能区分的有些问题,但是可以省很多功夫。因为模块之间耦合的比较严重,修改的话不符合开放封闭原则,容易牵一发动全身。

JackMariano avatar Jun 22 '22 03:06 JackMariano

我觉得reactor模式代码有问题。代码中将读事件插入阻塞队列中后有一个while(true)循环一直等到线程从阻塞队列中取此http链接后接受数据后才break。相当于阻塞队列中永远至多有一个http连接。

请教老哥觉得怎么修改才合理,能不能直接删掉这个while呢 我觉得修改的话需要把原本主线程执行的逻辑挪到线程池模块,比如关闭连接和删除定时器。但是这里的Webserver类是包含线程池的头文件的,如果threadpool中的工作线程需要调这个定时器删除逻辑的话,应该引入Webserver的头文件,然后把Webserver对象传入。但是这样会造成头文件循环依赖相互包含,我想的是把Webserver类维护定时器的职能拆分出来独立为一个模块,然后Webserver和threadpool同时包含他的实例化对象,让工作线程读完之后自己判断是否删除定时器以及关闭连接。主线程只负责监听事件即可。

我自己的项目HAHA-WebServer,是参考别人的做法,把evenloop单独抽出来,定时器部分直接交给eventloop,相关的删除操作则是由webserver对象对eventloop注册回调函数去完成,这样就不耦合了

tomatowithpotato avatar Jun 22 '22 03:06 tomatowithpotato

我觉得这个reacotor之所以这样写很大可能是因为作者偷懒了,先写的模拟proactor。在这个框架上写reactor,所以职能区分的有些问题,但是可以省很多功夫。因为模块之间耦合的比较严重,修改的话不符合开放封闭原则,容易牵一发动全身。

确实,作者应该也是知道这些问题,可能单纯不想改把

tomatowithpotato avatar Jun 22 '22 03:06 tomatowithpotato

我觉得reactor模式代码有问题。代码中将读事件插入阻塞队列中后有一个while(true)循环一直等到线程从阻塞队列中取此http链接后接受数据后才break。相当于阻塞队列中永远至多有一个http连接。

请教老哥觉得怎么修改才合理,能不能直接删掉这个while呢 我觉得修改的话需要把原本主线程执行的逻辑挪到线程池模块,比如关闭连接和删除定时器。但是这里的Webserver类是包含线程池的头文件的,如果threadpool中的工作线程需要调这个定时器删除逻辑的话,应该引入Webserver的头文件,然后把Webserver对象传入。但是这样会造成头文件循环依赖相互包含,我想的是把Webserver类维护定时器的职能拆分出来独立为一个模块,然后Webserver和threadpool同时包含他的实例化对象,让工作线程读完之后自己判断是否删除定时器以及关闭连接。主线程只负责监听事件即可。

我自己的项目HAHA-WebServer,是参考别人的做法,把evenloop单独抽出来,定时器部分直接交给eventloop,相关的删除操作则是由webserver对象对eventloop注册回调函数去完成,这样就不耦合了

已经star ^-^

JackMariano avatar Jun 22 '22 03:06 JackMariano

我觉得reactor模式代码有问题。代码中将读事件插入阻塞队列中后有一个while(true)循环一直等到线程从阻塞队列中取此http链接后接受数据后才break。相当于阻塞队列中永远至多有一个http连接。

请教老哥觉得怎么修改才合理,能不能直接删掉这个while呢 我觉得修改的话需要把原本主线程执行的逻辑挪到线程池模块,比如关闭连接和删除定时器。但是这里的Webserver类是包含线程池的头文件的,如果threadpool中的工作线程需要调这个定时器删除逻辑的话,应该引入Webserver的头文件,然后把Webserver对象传入。但是这样会造成头文件循环依赖相互包含,我想的是把Webserver类维护定时器的职能拆分出来独立为一个模块,然后Webserver和threadpool同时包含他的实例化对象,让工作线程读完之后自己判断是否删除定时器以及关闭连接。主线程只负责监听事件即可。

我自己的项目HAHA-WebServer,是参考别人的做法,把evenloop单独抽出来,定时器部分直接交给eventloop,相关的删除操作则是由webserver对象对eventloop注册回调函数去完成,这样就不耦合了

已经star ^-^

多谢捏,这是我瞎写的项目,主要为了应付秋招的^-^

tomatowithpotato avatar Jun 22 '22 03:06 tomatowithpotato

@huboy-zhao 奇怪了~我测试是删掉读事件的while后正常,只有删掉写事件的while之后,webbench测试才会出现你说的这种情况?

HandsomeJims avatar Jun 30 '22 12:06 HandsomeJims

我对项目做了修改,将webserver的部分职能移到了httpconn 模块。删除掉了Reactor模式的while循环阻塞,改为在子线程中结束连接,但是压测结果没有太大提升,可能是因为webbench的请求方式的原因,io的耗时太短。有兴趣的可以看看 https://github.com/huboy-zhao/MyWebserver

JackMariano avatar Aug 23 '22 09:08 JackMariano

我对项目做了修改,将webserver的部分职能移到了httpconn 模块。删除掉了Reactor模式的while循环阻塞,改为在子线程中结束连接,但是压测结果没有太大提升,可能是因为webbench的请求方式的原因,io的耗时太短。有兴趣的可以看看 https://github.com/huboy-zhao/MyWebserver

想请教一下,如果线程池中的线程也可以结束连接,删除定时器。那么从定时器堆(链表)中删除定时器就可能是race condition, 可能就需要锁了。但是原代码的实现是只有主线程才能删除定时器,所以不存在并发问题。不知道我的理解对不对。

greyjoy-wh avatar Jul 14 '23 07:07 greyjoy-wh

我对项目做了修改,将webserver的部分职能移到了httpconn 模块。删除掉了Reactor模式的while循环阻塞,改为在子线程中结束连接,但是压测结果没有太大提升,可能是因为webbench的请求方式的原因,io的耗时太短。有兴趣的可以看看 https://github.com/huboy-zhao/MyWebserver

你好,请问你这里压测结果没有太大提升指的是修改后的reactor相比于原来的reactor没有提升,还是说相比于proactor没有太大提升,就是具体结果是哪一种最好呢?因为这里我也有同样的疑问所以请教一下。

jeffreyyyyyyyyy avatar Sep 17 '23 09:09 jeffreyyyyyyyyy

真心提问:为什么理论上说模拟的proactor会比reactor慢呢,模拟proactor中主线程是异步线程,按理说应该至少不会比多个reactor并行慢呀。 等个大佬解释一下。

proactor里面读数据没有使用异步aio_read(),似乎是同步读取的。

wxCppro avatar Oct 04 '23 09:10 wxCppro

你们觉得代码中的注释proactor和reactor没有问题吗?怎么看出来下面的m_actormodel==1时是reactor,等于0时是proactor?我感觉搞反了吧,proactor不是异步的通过回调函数处理的吗?reactor是等待IO结束后同步处理的,我理解的对吗?

void WebServer::dealwithwrite(int sockfd)
{
    util_timer *timer = users_timer[sockfd].timer;
    //reactor
    if (1 == m_actormodel)
    {
        if (timer)
        {
            adjust_timer(timer);
        }

        m_pool->append(users + sockfd, 1);

        while (true)
        {
            if (1 == users[sockfd].improv)
            {
                if (1 == users[sockfd].timer_flag)
                {
                    deal_timer(timer, sockfd);
                    users[sockfd].timer_flag = 0;
                }
                users[sockfd].improv = 0;
                break;
            }
        }
    }
    else
    {
        //proactor
        if (users[sockfd].write())
        {
            LOG_INFO("send data to the client(%s)", inet_ntoa(users[sockfd].get_address()->sin_addr));

            if (timer)
            {
                adjust_timer(timer);
            }
        }
        else
        {
            deal_timer(timer, sockfd);
        }
    }
}

image

yllgl avatar Nov 30 '23 08:11 yllgl