【libevent】bufferevent的并发访问问题

news/2024/11/17 17:38:57/文章来源:https://www.cnblogs.com/LiBlog--/p/18293998

一、问题

在使用libevent实现websocket服务器时,发生了并发访问的问题。

服务器程序功能主要包括实时响应Websocket客户端的控制请求,同时发送温度到客户端。

现象:

不加上温度发送功能时,程序正常运行

加上温度发送功能后,就会出现段错误,而且检查后发现bufferevent并不为空

二、原因

在我的代码中,temp_cb()用于发送温度,read_cb()用于读取客户端发送来的数据(包括连接请求和led控制等),在没有加上温度发送功能前,程序中bufferevent只用处理错误事件和接收数据事件,加上后还要处理发送温度的事件。如果在发送温度事件时,同时可能有其他事件或回调函数正在修改或访问相同的 bev 变量,可能会导致竞态条件或意外的状态更改,从而引发段错误,导致并发问题。

代码如下:

static void temp_cb(evutil_socket_t fd, short events, void *arg)
{struct bufferevent		*bev = (struct bufferevent *)arg;if (!bev) {log_error("Received NULL buffer event in temp_cb\n");return;}send_temperature(bev);
}static void read_cb (struct bufferevent *bev, void *ctx)
{wss_session_t              *session = bev->cbarg;if( !session->handshaked ){do_wss_handshake(session);return ;}do_parser_frames(session);return ;
}static void event_cb (struct bufferevent *bev, short events, void *ctx)
{wss_session_t              *session = bev->cbarg;if( events&(BEV_EVENT_EOF|BEV_EVENT_ERROR) ){if( session )log_warn("remote client %s closed\n", session->client);bufferevent_free(bev);}return ;
}static void accept_cb(struct evconnlistener *listener, evutil_socket_t fd, struct sockaddr *addr, int len, void *arg)
{struct event_base               *ebase = arg;struct bufferevent 				*recv_bev, *send_bev;struct event            		*temp_event = NULL;struct timeval       			tv={10, 0};struct sockaddr_in              *sock = (struct sockaddr_in *)addr;wss_session_t                   *session;if( !(session = malloc(sizeof(*session))) ){log_error("malloc for session failure:%s\n", strerror(errno));close(fd);return ;}memset(session, 0, sizeof(*session));snprintf(session->client, sizeof(session->client), "[%d->%s:%d]", fd, inet_ntoa(sock->sin_addr), ntohs(sock->sin_port));log_info("accpet new socket client %s\n", session->client);bev_accpt = bufferevent_socket_new(ebase, fd, BEV_OPT_CLOSE_ON_FREE|BEV_OPT_DEFER_CALLBACKS);if( !bev_accpt ){log_error("create bufferevent for client for %s failed\n", session->client);return;}session->bev = bev_accpt;bufferevent_setcb(bev_accpt, read_cb, NULL, event_cb, session);bufferevent_enable(bev_accpt, EV_READ|EV_WRITE);temp_event = event_new(ebase, -1, EV_PERSIST, temp_cb, bev_accpt);if (!temp_event){log_error("failed to create temp event\n");return;}event_add(temp_event, &tv);return;
}

三、分析

libevent是通过I/O多路复用来实现高效的事件处理,事件循环(event loop)会不断调用底层的 I/O 多路复用函数( selectpollepoll),等待事件的发生。

libevent本身确实是一个单线程事件驱动模型。但是也可能出现并发问题:

  1. 多线程环境:尽管 libevent 的核心是单线程的,但如果程序是多线程的,并且多个线程尝试访问和操作同一个 bufferevent,就会出现并发问题。
  2. 事件回调重入:如果事件回调函数执行的时间较长,而在此期间另一个事件被触发并尝试访问同一个资源,可能会导致重入问题。
  3. 非线程安全代码:即使在单线程环境中,某些操作可能会触发不安全的并发访问,例如在回调中操作全局变量或共享资源。

四、解决方法

解决并发访问的问题最常用的方法是加锁,libevent 提供了一些机制来确保 bufferevent 在多线程环境下的安全性。例如,可以使用 bufferevent_lockbufferevent_unlock 函数来显式地对 bufferevent 进行加锁和解锁操作。

我选择的方式是把接收的bufferevent和发送的bufferevent分开,将接收和发送操作独立进行,避免一个操作阻塞另一个操作,提高整体响应速度。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/741717.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

manim边学边做--MarkupText

manim中主要有3个用于显示文本内容的对象,前两篇已经介绍过Text和Paragraph。本篇介绍最后一个MarkupText,与前两个不同的是,MarkupText的文本中支持实用一些HTML的语法,因此,它的表现力更胜前两个。MarkupText在manim各个模块中的位置大致如上图中所示。 1. 主要参数 Mar…

opc ua设备数据 转MQTT项目案例

目录 1 案例说明 1 2 VFBOX网关工作原理 1 3 准备工作 2 4 配置VFBOX网关采集OPC UA的数据 2 5 用MQTT协议转发数据 4 6 配置参数说明 4 7 上报内容配置 5 8 其他说明 8 9 案例总结 8 1 案例说明设置网关采集OPC UA设备数据 把采集的数据转成MQTT协议转发给其他系统。2 VFBOX网…

讲师招募 | Apache DolphinScheduler Meetup诚邀您共建开源!

随着Apache DolphinScheduler在全球范围内的快速发展,我们的用户群体和社区活动也在不断扩大。 为了进一步丰富我们的社区内容,分享更多有价值的知识和经验,我们诚挚地邀请您加入我们,成为Apache DolphinScheduler社区的分享嘉宾。 Meetup是什么? Apache DolphinScheduler…

Delta Sharing 连接测试

Power BI 方式: 下载地址: Power BI 客户端下载Url:下载 | Microsoft Power BI连接到 Databricks若要使用 Delta Sharing 连接器连接到 Azure Databricks,请执行以下操作:使用文本编辑器打开共享的凭据文件以检索终结点 URL 和令牌。打开 Power BI Desktop。 数据库连接;…

stm32时钟源

stm32时钟源 目录stm32时钟源MCU提供5种时钟源(1) HSE高速外部时钟(2) HSI高速内部时钟(3) LSE低速外部时钟(4) LSI低速内部时钟(5) PLL倍频锁相环定时器是挂载在总线下,而不同的总线的频率是不同的,而总线的频率是由时钟提供,而时钟的提供者又各不相同,所以必须要提前了解…

Easyadmin创建新的目录菜单步骤,Curd创建方法介绍

应用easyadmin创建文件目录,可以使用复制原有的目录,也可以使用CURD方法创建,一般为了防止复制后,修改出错或漏改,建议使用CURD方法,此方法不仅快捷方便,也因存在设计数据库,便于后面开发。 1.创建CURD命令行,创建数据表。 2.打开Composer,注意版本,此处使用php7.4,…

模块

模块1.第三方模块 Python内部提供的模块有限,所以在平时在开发的过程中,经常会使用第三方模块。 而第三方模块必须要先安装才能可以使用,使用第三方模块的行为就是去用别人写好并开源出来的py代码,这样自己拿来就用,不必重复造轮子了。 下面介绍常见的3种安装第三方模块的…

中国式报表不会做?用这款免费可视化工具3分钟搞定复杂报表

1. 什么是中国式报表? 中国式报表是一种中国独有的复杂报表,有格式复杂、计算复杂、数据来源复杂等特点,并且还有多样化的功能要求,例如图形、联动、回填等。因此许多国外报表工具在制作中国式报表方便表现得有些“水土不服”,那么我们该如何更加轻松地做出可靠、实用的报…

面试官:Java对象引用都有哪些类型?

面试连环call: 1. Java对象引用都有哪些类型? 2. Java参数传递是值传递还是引用传递? 为什么? 3. Java对象引用访问方式有哪些?哈喽,大家好🎉,我是世杰。 本文我为大家介绍面试官经常考察的「Java对象引用相关内容」照例在开头留一些面试考察内容~~ 面试连环callJava对…

机器学习策略篇:详解如何使用来自不同分布的数据,进行训练和测试(Training and testing on different distributions)

如何使用来自不同分布的数据,进行训练和测试 深度学习算法对训练数据的胃口很大,当收集到足够多带标签的数据构成训练集时,算法效果最好,这导致很多团队用尽一切办法收集数据,然后把它们堆到训练集里,让训练的数据量更大,即使有些数据,甚至是大部分数据都来自和开发集、…

Matebook14 2020款 更换固态(全流程)

Matebook14 2020款 更换固态全流程 因为工作的原因需要升级存储,我的老款的Matebook14只有512G。网络上的中文教程普遍比较古老。特此写下这篇笔记希望能帮助到有需要的朋友。 工具螺丝刀(四花00和六花T4) 新的固态硬盘 U盘(容量不小于1G) 移动硬盘(容量不能小于你的系统…

Linux安装JDK详细教程

Linux安装JDK详细教程(图文教程) 这里介绍两种方式:yum安装方式和手动安装1、yum安装 1.1 查看JDK版本,找到你想要安装的JDK版本,这里以 JDK1.8 为例 输入命令:yum -y list java*1.2 安装JDK1.8 输入命令:yum install -y java-1.8.0-openjdk.x86_64 没权限执行这行:sud…