C/C++精品项目之图床共享云存储(2):MySql连接池

一:介绍

C/C++精品项目之图床共享云存储(1)

我们项目的第一个文章讲解了很多的基础组件,包括线程池。我们都知道线程池是为了资源的复用,提高效率。而我们的MySql连接池也是一样的,是为了维持管理固定数量的连接,复用连接资源。

维持:我们维持与数据库的TCP长连接。

为什么:为了资源复用,更快的系统相应。

为什么不创建多条链接:我们会并发执行SQL语句,带来的副作用就是需要考虑事务。我们在同一条连接上是串行执行SQL语句的。

二:同步连接和异步连接

通俗的来讲,同步就是阻塞等待,异步就是不需要等待。那么对于连接池来说的话,我们同步和异步都使用在哪里呢?

同步等待连接的返回:会阻塞当前线程进行等待。

异步获取连接的返回:我们另起一个线程进行等待,如异步事件等待:reactor,proactor。

        在同步连接池中我们将会创建几个与数据库的连接,我们会同步创建,并且等他们全部返回,我们同步连接池中创建的数量是表示我们当前最多允许几个线程并发使用连接进行执行。(这些都会在后面的代码讲解中讲清楚)我们执行这些代码是在服务器初始化的时候,进行初始化资源。

        在异步连接池中我们将创建一个线程池,在这个线程池中,每个线程都会被分配一个与数据库的连接,而在主线程中,我们接收客户端发送来的请求,并把他们发送来的请求全部放到一个队列中去,然后线程池从这个队列中进行获取数据。我们异步连接池中,表示当前最多允许几个连接同时进行执行SQL语句。我们这个代码是在启动服务之后。

在这个异步中,如果我们想获取他们SQL返回来的结果的话,那么我们需要使用future这个来获取他们的结果。

三:代码部分

1:与数据库进行连接的类 CDBConn

        我们下面就是他的全部代码,其中包括一些初始化,以及连接上之后可以做出的命令:创建删除查找等。首先我们看一下成员变量,其中包括返回的行数,对应的连接池,以及连接的实例。这个连接池看不懂先不要管,在后面就懂了。

class CDBConn {     //与数据库具体的连接public:CDBConn(CDBPool *pDBPool);virtual ~CDBConn();int Init();// 创建表bool ExecuteCreate(const char *sql_query);// 删除表bool ExecuteDrop(const char *sql_query);// 查询CResultSet *ExecuteQuery(const char *sql_query);bool ExecutePassQuery(const char *sql_query);bool ExecuteUpdate(const char *sql_query, bool care_affected_rows = true);uint32_t GetInsertId();// 开启事务bool StartTransaction();// 提交事务bool Commit();// 回滚事务bool Rollback();// 获取连接池名const char *GetPoolName();MYSQL *GetMysql() { return mysql_; }int GetRowNum() { return row_num; }private:int row_num = 0;CDBPool *db_pool_; // to get MySQL server informationMYSQL *mysql_;     // 对应一个连接char escape_string_[MAX_ESCAPE_STRING_LEN + 1];
};

我们先看一下inti初始化函数,因为这个类是与数据库进行连接的类,所以我们在这里会进行连接的具体操作: 创建一个实例,设置他的自动重连,然后通过与他进行连接,并且把这个连接上来的实例进行保存。那么我们每次调用init就表示我们创建了一个与数据库连接的实例。

int CDBConn::Init() {       //总的来说就是进行初始化一个实例,通过这个实例进行与数据库的连接。mysql_ = mysql_init(NULL); // mysql_标准的mysql c client对应的api   也就是进行与数据库的连接  进行初始化if (!mysql_) {LogError("mysql_init failed"); return 1;}//设置一些连接的选项。int reconnect = 1;mysql_options(mysql_, MYSQL_OPT_RECONNECT,&reconnect); // 配合mysql_ping实现自动重连mysql_options(mysql_, MYSQL_SET_CHARSET_NAME,"utf8mb4"); // utf8mb4和utf8区别// ip 端口 用户名 密码 数据库名             进行连接if (!mysql_real_connect(mysql_, db_pool_->GetDBServerIP(),db_pool_->GetUsername(), db_pool_->GetPasswrod(),db_pool_->GetDBName(), db_pool_->GetDBServerPort(),NULL, 0)) {LogError("mysql_real_connect failed: {}", mysql_error(mysql_));return 2;}return 0;
}

         我们举一个与数据库操作的例子:比如说这个查询操作。我们首先先重连一下,然后直接执行我们的sql语句,由于是查询当然会返回一些结果,于是我们需要res来存储我们得到的结果。然后通过CResultSet来存储我们得到的结果。对于其他的操作都一样而且还比这个少。

//我们通过执行查询的语句,并且接收返回的结果,并用CResultSet存储返回的结果。
CResultSet *CDBConn::ExecuteQuery(const char *sql_query) {mysql_ping(mysql_);row_num = 0;if (mysql_real_query(mysql_, sql_query, strlen(sql_query))) {LogError("mysql_real_query failed: {}, sql:{}",  mysql_error(mysql_), sql_query);return NULL;}// 返回结果MYSQL_RES *res = mysql_store_result(mysql_); // 返回结果 https://www.mysqlzh.com/api/66.htmlif (!res) // 如果查询未返回结果集和读取结果集失败都会返回NULL{LogError("mysql_store_result failed: {}", mysql_error(mysql_));return NULL;}row_num = mysql_num_rows(res);// LogInfo("row_num: {}", row_num;CResultSet *result_set = new CResultSet(res); // 存储到CResultSetreturn result_set;
}

2:连接池CDBPool

我们这个连接池中是管理CDBconn的,下面就是我们所看到的全部函数。首先看一下成员变量:包括数据库的ip端口用户名等,除了这些就是我们的连接数量和最大连接数量。以及两个list列表,空闲和繁忙。

//我们同步的连接池是在初始化的时候,我们服务器与数据库进行多个连接,然后分配到不同的线程中去,而主线程只是处理客户端发送来的消息,将他们分发到不同的线程中。
//我们创建一个连接池,这个连接池中包含好几个线程,而且每个线程配备一个与数据库的连接。
//我们的线程池被阻塞:没有任务会阻塞等待。而连接池被阻塞:没有任务和当前连接正在被占用。
//我们一个连接池只能是对应一个数据库,比如我们有data1,data2,data3,这三个数据库,那么我们创建一个连接池,只能连接其中的一个。
//想要全部连接,那么就需要创建三个连接池。
class CDBPool { // 只是负责管理连接CDBConn,真正干活的是CDBConnpublic:CDBPool() {} // 如果在构造函数做一些可能失败的操作,需要抛出异常,外部要捕获异常CDBPool(const char *pool_name, const char *db_server_ip,uint16_t db_server_port, const char *username, const char *password,const char *db_name, int max_conn_cnt);virtual ~CDBPool();int Init(); // 连接数据库,创建连接CDBConn *GetDBConn(const int timeout_ms = 0); // 获取连接资源void RelDBConn(CDBConn *pConn);               // 归还连接资源const char *GetPoolName() { return pool_name_.c_str(); }const char *GetDBServerIP() { return db_server_ip_.c_str(); }uint16_t GetDBServerPort() { return db_server_port_; }const char *GetUsername() { return username_.c_str(); }const char *GetPasswrod() { return password_.c_str(); }const char *GetDBName() { return db_name_.c_str(); }private:string pool_name_;          // 连接池名称string db_server_ip_;       // 数据库ipuint16_t db_server_port_;   // 数据库端口string username_;           // 用户名string password_;           // 用户密码string db_name_;            // db名称int db_cur_conn_cnt_;       // 当前启用的连接数量int db_max_conn_cnt_;       // 最大连接数量list<CDBConn *> free_list_; // 空闲的连接list<CDBConn *> used_list_; // 记录已经被请求的连接std::mutex mutex_;std::condition_variable cond_var_;    //条件变量bool abort_request_ = false;          // 终止请求
};
CDBPool::CDBPool(const char *pool_name, const char *db_server_ip,uint16_t db_server_port, const char *username,const char *password, const char *db_name, int max_conn_cnt) {     //初始化连接池,将数据库的连接信息保存下来,并且指定多少个线程,或者说是多少个连接并发执行。pool_name_ = pool_name;db_server_ip_ = db_server_ip;db_server_port_ = db_server_port;username_ = username;password_ = password;db_name_ = db_name;db_max_conn_cnt_ = max_conn_cnt;    //db_cur_conn_cnt_ = MIN_DB_CONN_CNT; // 最小连接数量
}

 首先就是init初始化函数,我们看到根据最小的数量我们创建了好几个连接。

int CDBPool::Init() {       // 创建固定最小的连接数量for (int i = 0; i < db_cur_conn_cnt_; i++) {CDBConn *db_conn = new CDBConn(this);int ret = db_conn->Init();          //创建一个与数据库的连接。if (ret) {delete db_conn;return ret;}free_list_.push_back(db_conn);      //刚刚创建好的,肯定是空闲连接的啊}return 0;
}

         这个函数中看似代码很多其实比较简单。首先大致讲一下,我们这个函数是我们要获取空闲连接,因为我们有空闲列表,我们与数据库进行连接肯定就是从空闲连接上进行发送数据。如果有空闲连接我们直接进行拿取,如果没有那么需要我们进行创建,如果没有满足最大数量,那么我们就创建,然后再拿取。如果已经满足最大数量了,那么就需要我们进行等待,这里就分为了无限等待和等待时间。

//梳理一下逻辑:首先就是看空闲队列中有没有,如果有,则直接拿取一个使用。如果没有,那就看看是否到最大连接了,如果没到最大连接//那就创建一个,放到空闲队列中,继续拿取。当到了最大连接了,那我们看看是无限等待还是等待一会儿,当无限等待,那就wait,如果等待时间,那就wait_for
CDBConn *CDBPool::GetDBConn(const int timeout_ms) {     //拿取连接std::unique_lock<std::mutex> lock(mutex_);if (abort_request_) {LogWarn("have aboort"); return NULL;}if (free_list_.empty()) // 2 当没有连接可以用时{// 第一步先检测 当前连接数量是否达到最大的连接数量if (db_cur_conn_cnt_ >= db_max_conn_cnt_) // 等待的逻辑{// 如果已经到达了,看看是否需要超时等待if (timeout_ms <= 0) // 死等,直到有连接可以用 或者 连接池要退出{cond_var_.wait(lock, [this] {// 当前连接数量小于最大连接数量 或者请求释放连接池时退出return (!free_list_.empty()) | abort_request_;});} else {// return如果返回 false,继续wait(或者超时),// 如果返回true退出wait 1.m_free_list不为空 2.超时退出// 3. m_abort_request被置为true,要释放整个连接池cond_var_.wait_for(lock, std::chrono::milliseconds(timeout_ms),[this] { return (!free_list_.empty()) | abort_request_; });// 带超时功能时还要判断是否为空if (free_list_.empty()) // 如果连接池还是没有空闲则退出{return NULL;}}if (abort_request_) {LogWarn("have abort"); return NULL;}} else // 还没有到最大连接则创建连接{CDBConn *db_conn = new CDBConn(this); //新建连接int ret = db_conn->Init();if (ret) {LogError("Init DBConnecton failed"); delete db_conn;return NULL;} else {free_list_.push_back(db_conn);          //创建成功则放入到空闲队列中,继续下面拿取的操作。db_cur_conn_cnt_++;// log_info("new db connection: %s, conn_cnt: %d\n",// m_pool_name.c_str(), m_db_cur_conn_cnt);}}}//有空闲连接,那么我们直接拿取使用。CDBConn *pConn = free_list_.front(); // 获取连接free_list_.pop_front(); // STL 吐出连接,从空闲队列删除return pConn;
}

         这个函数就是我们进行将不用的连接进行返回,其中为了避免重复放回,需要进行一下判断。

//将不使用的连接,归还给连接池:空闲队列中。首先为了避免重复归还,我们将这个连接从空闲队列中找一下,如果找到则说明已经归还。
//如果不存在那么我们就插入到空闲队列中,并通知取队列:GetDBConn。
void CDBPool::RelDBConn(CDBConn *pConn) {       std::lock_guard<std::mutex> lock(mutex_);list<CDBConn *>::iterator it = free_list_.begin();for (; it != free_list_.end(); it++) // 避免重复归还{if (*it == pConn) {break;}}if (it == free_list_.end()) {// used_list_.remove(pConn);free_list_.push_back(pConn);cond_var_.notify_one(); // 通知取队列} else {LogWarn("RelDBConn failed");  // 不再次回收连接}
}

3:连接池的管理者 CDBManager

我们上面已经说过,我们一个连接池只能连接一个数据库,因此如果我们需要连接多个数据库的话,那么就需要我们创建多个连接池,然后我们需要同一管理这些连接池,于是出现了CDBManager。

// manage db pool (master for write and slave for read)
class CDBManager {public:virtual ~CDBManager();static void SetConfPath(const char *conf_path);static CDBManager *getInstance();int Init();CDBConn *GetDBConn(const char *dbpool_name);void RelDBConn(CDBConn *pConn);private:CDBManager();private:static CDBManager *s_db_manager;map<string, CDBPool *> dbpool_map_;static std::string conf_path_;
};

        在这个函数中我们会传进去一个参数配置文件,在这个文件中我们将存储数据库的ip端口等,以及全部需要连接的数据库等,我们再这个配置文件中将全部加载出来,然后直接创建多个连接池。然后将这些创建好的连接池,放入到一个map列表中。

int CDBManager::Init() {CConfigFileReader config_file;     //读取配置文件if(config_file.ParseConf(conf_path_.c_str()) != 0) {std::cout << conf_path_ << " no exist\n";return -1;}char *db_instances = config_file.GetConfigName("DBInstances");if (!db_instances) {LogError("not configure DBInstances"); return 1;}char host[64];char port[64];char dbname[64];char username[64];char password[64];char maxconncnt[64];CStrExplode instances_name(db_instances, ',');for (uint32_t i = 0; i < instances_name.GetItemCnt(); i++) {char *pool_name = instances_name.GetItem(i);snprintf(host, 64, "%s_host", pool_name);snprintf(port, 64, "%s_port", pool_name);snprintf(dbname, 64, "%s_dbname", pool_name);snprintf(username, 64, "%s_username", pool_name);snprintf(password, 64, "%s_password", pool_name);snprintf(maxconncnt, 64, "%s_maxconncnt", pool_name);char *db_host = config_file.GetConfigName(host);char *str_db_port = config_file.GetConfigName(port);char *db_dbname = config_file.GetConfigName(dbname);char *db_username = config_file.GetConfigName(username);char *db_password = config_file.GetConfigName(password);char *str_maxconncnt = config_file.GetConfigName(maxconncnt);LogInfo("db_host:{}, db_port:{}, db_dbname:{}, db_username:{}, db_password:{}", db_host, str_db_port, db_dbname, db_username, db_password);if (!db_host || !str_db_port || !db_dbname || !db_username ||!db_password || !str_maxconncnt) {LogError("not configure db instance: {}", pool_name);return 2;}int db_port = atoi(str_db_port);int db_maxconncnt = atoi(str_maxconncnt);CDBPool *pDBPool = new CDBPool(pool_name, db_host, db_port, db_username,db_password, db_dbname, db_maxconncnt);if (pDBPool->Init()) {LogError("init db instance failed: {}", pool_name);return 3;}dbpool_map_.insert(make_pair(pool_name, pDBPool));}return 0;
}

 这个就是在所有的连接池中封装的一层,直接从某一个连接池中拿取连接,封装这一层之后非常方便我们拿取连接。下面的归还连接也是一样的。

CDBConn *CDBManager::GetDBConn(const char *dbpool_name) {map<string, CDBPool *>::iterator it = dbpool_map_.find(dbpool_name); // 主从if (it == dbpool_map_.end()) {return NULL;} else {return it->second->GetDBConn();}
}void CDBManager::RelDBConn(CDBConn *pConn) {if (!pConn) {return;}map<string, CDBPool *>::iterator it = dbpool_map_.find(pConn->GetPoolName());if (it != dbpool_map_.end()) {it->second->RelDBConn(pConn);}
}

4:自动归还

这个类就是我们进行自动的归还,因为我们这个连接中含有全部的信息,包括这个连接所在的连接池。我们通过manger中的reldbcon,在里面通过调用conn的信息就可以进行归还操作。

// 目的是在函数退出后自动将连接归还连接池
class AutoRelDBCon {public:AutoRelDBCon(CDBManager *manger, CDBConn *conn): manger_(manger), conn_(conn) {}~AutoRelDBCon() {if (manger_) {manger_->RelDBConn(conn_);}} //在析构函数规划private:CDBManager *manger_ = NULL;CDBConn *conn_ = NULL;
};

四:整体图

        回顾一下,我们通过连接池先init后,创建多个连接,将他们全部放入到空闲队列中,然后通过拿取空闲连接,放入到线程中(这里未体现)只是简单的拿出来了,还有个就是归还函数。后面就是Manger管理全部的连接池,通过解析配置文件加载所有的连接池,并且在这里封装了全部的连接池的拿取以及放回函数,非常的方便。

感谢大家的观看!https://github.com/0voice

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

如若内容造成侵权/违法违规/事实不符,请联系一条长河网进行投诉反馈,一经查实,立即删除!

相关文章

网安数学基础-同余关系

文章目录 参考等价关系实例 同余同余和等价同余的运算 乘法逆元一次同余方程消去律 剩余类中国剩余定理欧拉函数欧拉定理 费马小定理 参考 【一口气学完】密码学的数学基础2&#xff0c;《同余关系》&#xff0c;一小时学完 等价关系 三角形里的全等关系 等价关系定义 下面这…

工业主板市场现状和主要市场驱动因素概述

工业主板市场是计算机硬件行业中的一个重要细分市场&#xff0c;专注于供应专为工业应用而设计的主板。与消费级主板不同&#xff0c;工业主板可承受极端条件&#xff0c;包括高温、连续运行以及暴露在灰尘和潮湿环境中。工业主板对于制造、自动化、医疗、国防和能源等行业中使…

舌尖上的传统美味 —— 食家巷白吉饼

第一眼看到食家巷白吉饼&#xff0c;就被它朴实的外表所吸引。圆润的形状&#xff0c;淡淡的麦色&#xff0c;没有过多的装饰&#xff0c;却散发着一种让人安心的质朴之美。 &#x1f388;拿起一个白吉饼&#xff0c;轻轻一掰&#xff0c;“咔” 的一声&#xff0c;那酥脆的外…

OpenHarmony-1.启动流程

OpenHarmony启动流程 1.kernel的启动 流程图如下所示&#xff1a;   OpenHarmony(简称OH)的标准系统的底层系统是linux&#xff0c;所以调用如下代码&#xff1a; linux-5.10/init/main.c: noinline void __ref rest_init(void) {struct task_struct *tsk;int pid;rcu_sch…

ANR分析实例

目录 一、ANR出的问题原因 二、日志分析 2.1 CPU 负载 2.2 内存 2.3 堆栈信息 三、案例分析 3.1 主线程无卡顿&#xff0c;处于正常状态堆栈 3.2 主线程执行耗时操作 3.3 主线程被锁阻塞 3.4 CPU被抢占 3.5 内存紧张导致ANR 3.6 系统服务超时导致ANR 3.7 Input dis…

知识库搭建:高企创新创业的智慧引擎与未来趋势

在当今这个科技迅猛发展的时代&#xff0c;高新技术企业&#xff08;简称“高企”&#xff09;作为推动经济社会进步的重要力量&#xff0c;正面临着前所未有的创新挑战与机遇。知识库&#xff0c;这一信息时代的关键工具&#xff0c;不仅汇聚了高企内部的丰富知识与经验&#…

D67【python 接口自动化学习】- python基础之数据库

day67 Python操作MySQL基础使用 学习日期&#xff1a;20241113 学习目标&#xff1a;MySQL数据库-- 136 Python操作MySQL基础使用 学习笔记&#xff1a; pymysql 创建MySQL的数据库链接 执行sql语句 总结 Python中使用第三方库&#xff1a;pymysql来操作MySQL&#xff0c;…

容器管理平台KubeSphere的使用

在之前的课程中我们是在minikube中安装了K8S环境,然后通过kubectl命令来管理K8S的,这种用命令来管理的方式虽然对程序员来说看起来很炫酷,但有时候用起来还是挺麻烦的。这节课程给大家介绍一款企业级容器管理平台KubeSphere,使用它能更方便地管理K8S中的容器,希望对大家有…

低代码可视化-uniapp气泡弹窗组件可视化-代码生成器

气泡弹窗组件是产品设计中常用的控件之一&#xff0c;以下是对uniapp气泡弹窗组件可视化的详细解析&#xff1a; 一、组件定义 气泡弹窗组件&#xff08;diy-popover &#xff09;指的是当触发某项操作时&#xff0c;在页面上方或特定位置展示的弹出层容器&#xff0c;容器内…

Unity3D学习FPS游戏(11)敌人AI巡逻(NavMesh)

前言&#xff1a;前面两篇博客已经实现了简单的敌人&#xff0c;但是呢&#xff0c;这样很无趣。因为敌人只会站在原地被攻击&#xff0c;所以本篇我们将实现敌人AI巡逻&#xff0c;让敌人动起来。 敌人AI巡逻 场景丰富一下导航网格NavMesh构建导航网格导航网格优化玩家被当作…

遇到“msvcr120.dll丢失”要怎么解决?详解msvcr120.dll的解决方法

遇到“msvcr120.dll丢失”错误通常表明你的系统缺少一个关键的DLL文件&#xff0c;这是Microsoft Visual C 2013的一部分。这个问题可能导致某些程序无法运行&#xff0c;影响电脑性能。不过&#xff0c;解决这一问题并不复杂。接下来&#xff0c;本文将向你展示几种简单的修复…

git相关知识

前言&#xff1a;在学习git之前首先需要了解几个概念&#xff1a;工作区&#xff0c;暂存区&#xff0c;版本库。 工作区&#xff1a;是电脑上写代码或者文件的目录。 暂存区&#xff1a;一般存放在.git目录下的index中&#xff0c;也称索引。&#xff08;git add&#xff09…

Spring Boot项目的配置文件有哪些?加载优先级谁最高?配置优先级谁最高?

目录 一. 结论 二. .prperties、.yml、.yaml 文件类型简介及注意点 2.1 文件简介 2.2. .prperties&#xff08;默认格式/传统格式&#xff09; 2.3 application.yml&#xff08;主流格式&#xff09; 2.4 application.yaml&#xff08;主流格式&#xff09; 2.5 配置文件…

蓝牙眼镜WT6900HA语音控制芯片方案:开启智能穿戴新篇章

前言&#xff1a; 随着科技的飞速发展&#xff0c;智能穿戴设备已经逐渐融入我们的日常生活。从智能手表到智能手环&#xff0c;再到如今的智能蓝牙眼镜&#xff0c;这些设备不仅改变了我们的生活方式&#xff0c;更引领了一场科技革命。 今天&#xff0c;我们要为大家介绍一款…

项目管理【02】项目开发完整指南

移动端项目开发完整指南&#xff1a;从需求到上线 在移动应用开发过程中&#xff0c;一个项目从最初的需求到最终上线&#xff0c;需要经过多个关键阶段。本文将详细介绍整个开发流程中的最佳实践&#xff0c;帮助开发团队更高效地完成项目。 一、准备阶段 项目启动前的准备工…

快手直播间采集教程,快手引流,快手截流,截流工具,直播间截流,快手直播间采集,获客系统,获客软件

功能&#xff1a; 1.输入快手直播间链接可一键监控直播间 2.可采集新进直播间的人 3.可采集直播间所有动作&#xff0c;包含&#xff1a;发弹幕的人和内容、送礼物的人和送的礼物、点亮爱心的人 4.可一键导出新进直播间的快手ID 5.可一键导出直播间动作列表&#xff0c;也可以筛…

sol机器人pump机器人如何实现盈利的?什么是Pump 扫链机器人?

什么是Pump 扫链机器人&#xff0c;它的盈利逻辑优化策略是什么&#xff1f; Pump 扫链机器人&#xff0c;通过智能化、自动化的买卖操作帮助投资者实现快速盈利。在此基础上&#xff0c;我们对该机器人的盈利逻辑进行了深度优化&#xff0c;涵盖了买入策略和止盈策略的各个方面…

【vue2】13.自定义指令

目录 自定义指令 自定义指令的作用? 自定义指令的使用步骤? 1. 注册 (全局注册 或 局部注册) 2. 标签上 v-指令名 使用 自定义指令 - 指令的值 1. 通过指令的值相关语法&#xff0c;可以应对更复杂指令封装场景 2. 指令值的语法&#xff1a; 自定义指令 - v-loading…

【LLM】Agent的相关Benchmark

note 文章目录 note一、SuperCLUE-Agent二、AgentBench三、跨系统benchmark&#xff1a;CRAB四、SWE-bench 一、SuperCLUE-Agent SuperCLUE-Agent是一个 Agent智能体中文原生任务能力测评基准&#xff0c;评估方面包括&#xff1a; 工具使用能力&#xff08;调用API、检索API、…

MATLAB用CNN-LSTM神经网络的语音情感分类深度学习研究

全文链接&#xff1a;https://tecdat.cn/?p38258 原文出处&#xff1a;拓端数据部落公众号 在语音处理领域&#xff0c;对语音情感的分类是一个重要的研究方向。本文将介绍如何通过结合二维卷积神经网络&#xff08;2 - D CNN&#xff09;和长短期记忆网络&#xff08;LSTM&…