【C++实现】 数据库连接池

2023-11-13


涉及知识


MySQL数据库编程、单例模式、queue队列容器、C++11多线程编程、线程互斥、线程同步通信和
unique_lock、基于CAS的原子整形、智能指针shared_ptr、lambda表达式、生产者-消费者线程模型

为什么要弄连接池


因为mysql 简历连接的时候需要底层先建立连接,说白了TCP需要三次握手吧,Mysql Server连接需要认证吧,Mysql Server 需要关闭连接回收资源,TCP需要四次挥手吧。
所以就是说,如果我们如果需要连接上数据库然后发送一条数据,需要做上面的这些工作。

在市场上比较流行的连接池包括阿里的druid,c3p0以及apache dbcp连接池,它们对于短时间内大量的数据库增删改查操作性能的提升是很明显的,但是它们有一个共同点就是,全部由Java实现的。

那么我们C++的也不必眼馋,自己动手实现基于C++代码的数据库连接池模块。

功能介绍


连接对象模块
Connection模块,这个模块负责实现连接mysql的部分,进行数据库增删查改接口的封装。

  • Connection初始化连接对象
  • connect 绑定ip,端口,用户,密码,库名称
  • update 传入string sql,进行实施
  • query 进行select查询,这里用不到
  • refreshAliveTime 设置每一个连接对象在队列里面呆着的起始时间
  • getAliveTime 获取对象从refreshAliveTime到如今的时间,单位ms

连接池模块模块
这个模块是一个单例对象ConnectionPool,负责封装一个个Connection。

  • 提供getConnection给外部,返回一个连接对象。
  • 内部实现produceConnectionTask,会多开一个线程检测连接对象,适当的进行扩容;
  • 内部实现 scannerConnectionTask,会多开一个线程,若是有超过初始化的连接对象长时间没有使用,就会进行释放。
  • 内部实现loadConfigFile ,会对mysql.ini配置文件进行读取,我们可以将各种信息记录到文件中,初始化连接池会进行调用读取。

成员变量讲解


ConnectionPool中会记录连接的库的ip地址,端口号,mysql用户名,密码,数据库名称; 以及 mysql初始连接数量mysql最大连接数量每一个连接对象在队列所能待的最大空闲时间获取连接的一个超时时间存储连接对象的队列保证互斥的锁条件变量总共有多少个连接池对象。

解释一下变量:
1.这里的_maxIdletime,_initSize,_maxSize,scannerConnectionTask就是通过这两个变量进行判断是否需要进行删减连接池中的连接对象。_maxIdletime标识的就是一个连接对象在队列中能呆着的最长时间。
2._connectionTimeout 是我们上层调用底层的连接池获取一个连接对象的最长等待时间。假如底层的连接对象都被获取了,那么我的线程就会在等待_connectionTimeout进行timeout一次。
3._connectionQue,_queueMutex,_cv 就是对临界资源的保护,生产者需要通过_cv确认是否需要添加新的连接对象入队列。消费者需要_cv判断此时队列是否还有连接对象可以被消费。
4._connectionCnt表示总共创建的连接对象。由于_connectionQue的大小只能说明此时有多少个连接对象还没有被使用,我们需要_connectionQue标识已经创建了多少的连接对象。_connectionQue是用来进行scannerConnectionTask,produceConnectionTask衡量的变量。

string _ip; // mysql ip地址
unsigned short _port; // mysql 端口号
string _username; // mysql 用户名
string _password; // mysql 密码
string _dbname; // 数据库名称
int _initSize;	 // mysql 初始连接量
int _maxSize;	 // mysql的最大连接量
int _maxIdletime;// 最大空闲时间
int _connectionTimeout; // 超时时间

queue<Connection*> _connectionQue; // 存储mysql链接的队列
mutex _queueMutex; // 维护连接队列线程安全的互斥锁
condition_variable _cv; // 队列条件变量
atomic_int _connectionCnt; // 记录连接所创建的connection的总量

代码剖析

Connection.h


#define _CRT_SECURE_NO_WARNINGS
#pragma once
#include<iostream>
using std::string;
#include<mysql.h>
using std::cout;
using std::endl;

#define LOG(str) \
	cout << __FILE__ << ":" << __LINE__ << " " << \
	__TIMESTAMP__ << " : " << str << endl;

class Connection
{
public:
	// 初始化数据库连接
	Connection();
	// 释放数据库连接资源
	~Connection();
	// 连接数据库
	bool connect(string ip, unsigned short port, string user, string password,
		string dbname);

	// 更新操作 insert、delete、update
	bool update(string sql);

	// 查询操作 select
	MYSQL_RES* query(string sql);

	// 刷新一下链接的时间
	void refreshAliveTime()
	{
		_alivetime = clock();
	}

	clock_t getAliveTime()
	{
		return clock();
	}
private:
	MYSQL* _conn; // 表示和MySQL Server的一条连接
	clock_t _alivetime; // 存活时间
};

Connection.cpp


这份Connection.cpp实际上就是对sql常用的功能的封装。


#include"Connection.h"
#include<iostream>

	// 初始化数据库连接
Connection::Connection()
{
	_conn = mysql_init(nullptr);
}
	// 释放数据库连接资源
Connection::~Connection()
{
	if (_conn != nullptr)
		mysql_close(_conn);
}
// 连接数据库
bool Connection::connect(string ip, unsigned short port, string user, string password,
	string dbname)
{
	MYSQL* p = mysql_real_connect(_conn, ip.c_str(), user.c_str(),
		password.c_str(), dbname.c_str(), port, nullptr, 0);
	return p != nullptr;
}
// 更新操作 insert、delete、update
bool Connection::update(string sql)
{
	if (mysql_query(_conn, sql.c_str()))
	{
		LOG("更新失败:" + sql);
		return false;
	}
	return true;
}
// 查询操作 select
MYSQL_RES* Connection::query(string sql)
{
	if (mysql_query(_conn, sql.c_str()))
	{
		LOG("查询失败:" + sql);
		return nullptr;
	}
	return mysql_use_result(_conn);
}




ConnectionPool.h


#pragma once
#include<string>
#include<queue>
using namespace std;
#include"Connection.h"
#include<mutex>
#include<thread>
#include<functional>
#include<atomic>
#include<condition_variable>
/*
实现连接池模块
*/

class ConnectionPool
{
public:
	static ConnectionPool* getConnectionPool();
	// 给外部提供接口,提供一个可用的空闲连接
	shared_ptr<Connection> getConnection();
	~ConnectionPool();
private:
	void operator=(const ConnectionPool&) = delete;
	ConnectionPool(const ConnectionPool&) = delete;
	ConnectionPool(); // 单例 构造函数私有化
	
	// 运行在独立的线程中,专门负责生产新连接
	void produceConnectionTask();

	// 扫描多余的空闲连接,超过maxIndleTime
	void scannerConnectionTask();

	// 从配置文件加载配置项
	bool loadConfigFile();

	string _ip; // mysql ip地址
	unsigned short _port; // mysql 端口号
	string _username; // mysql 用户名
	string _password; // mysql 密码
	string _dbname; // 数据库名称
	int _initSize;	 // mysql 初始连接量
	int _maxSize;	 // mysql的最大连接量
	int _maxIdletime;// 最大空闲时间
	int _connectionTimeout; // 超时时间

	queue<Connection*> _connectionQue; // 存储mysql链接的队列
	mutex _queueMutex; // 维护连接队列线程安全的互斥锁
	condition_variable _cv; // 队列条件变量
	atomic_int _connectionCnt; // 记录连接所创建的connection的总量
	//thread produce;
	//thread scanner;
	bool isRun = false;// 判断是否还在运行
};

ConnectionPool.cpp


#define _CRT_SECURE_NO_WARNINGS

#include"ConnectionPool.h"
#include"public.h"
ConnectionPool* ConnectionPool::getConnectionPool()
{
	static ConnectionPool pool;
	return &pool;
}
ConnectionPool::~ConnectionPool()
{
	isRun = true;
	_cv.notify_all();
}

// 单例 构造函数私有化
ConnectionPool::ConnectionPool()
{
	// 加载配置项
	if (!loadConfigFile())
	{
		return; // 日志信息里面有打印
	}

	
	// 创建初始的数量连接
	for (int i = 0; i < _initSize; ++i)
	{
		Connection* p = new Connection();
		p->connect(_ip,_port,_username,_password,_dbname);
		p->refreshAliveTime(); // 刷新一下开始空闲的起始时间
		_connectionQue.push(p);
		_connectionCnt++;
	}
	// 启动一个新的线程,作为连接生产者,绑定有一个成员变量,并且传入this指针才能使用
	thread produce(std::bind(&ConnectionPool::produceConnectionTask, this));
	produce.detach();
	 启动一个新的定时线程,扫描多余的空闲连接,超过maxIndleTime
	thread scanner(std::bind(&ConnectionPool::scannerConnectionTask, this));
	scanner.detach();
}

// 扫描多余的空闲连接,超过maxIndleTime
void ConnectionPool::scannerConnectionTask()
{
	for (;;)
	{
		if (isRun)
			return;
		// 直接睡_maxIdletime,起来就检测一次
		//this_thread::sleep_for(chrono::seconds(_maxIdletime));
		// 扫描整个队列
		unique_lock<mutex> lock(_queueMutex);
		while (_connectionCnt > _initSize)
		{
			if (isRun)
				return;
			// 若是每一个线程都占用着连接,此时扫描线程进来后检测到队列为空,就可以直接退出
			if (_connectionQue.empty())
			{
				break;
			}
			// 队头的时间是待在队列最长的
			Connection* p = _connectionQue.front();
			if (p->getAliveTime() >= _maxIdletime * 1000) // 60s 的话太长了,一般来说不会调用这里pop掉,6s的话这里会进行删除
			{
				_connectionQue.pop();
				_connectionCnt--;
				delete p; // 调用~Connection 释放连接
			}
			else
			{
				break;// 队头没有超过超时时间,那么没必要看了
			}
		}
	}
}

// 运行在独立的线程中,专门负责生产新连接
void ConnectionPool::produceConnectionTask()
{
	// 生产连接需要注意不能超过最大的量
	for (;;)
	{
		if (isRun)
			return;
		unique_lock<mutex> lock(_queueMutex); // 由于wait要释放锁,所以用unique_lock
		while (!isRun && !_connectionQue.empty())
		{
			if (isRun)
				return;
			_cv.wait(lock); // 等待队列变空,此时不需要生产
		}
		if (isRun)
			return;// 不能访问到任何主线程的容器。
		// 走到这里,说明需要生产者生产连接
	
		// 若常见的连接已经比最大的创建数都多了,就不再创建了,让他们等着其他连接用完,这里补充处理
		if (isRun && _connectionCnt < _maxSize)
		{
			// 这里是连接数量没有到达上线
			Connection* p = new Connection();
			p->connect(_ip, _port, _username, _password, _dbname);
			p->refreshAliveTime(); // 刷新一下开始空闲的起始时间
			_connectionQue.push(p);
			_connectionCnt++;
		}
		// 通知消费者线程可以消费,若是到达了最大值,也唤醒,因为可能有线程已经用完连接返回了
		_cv.notify_all();
	}
}
// 给外部提供接口,提供一个可用的空闲连接,消费者线程,消费者只会等待若干秒
shared_ptr<Connection> ConnectionPool::getConnection()
{
	unique_lock<mutex> lock(_queueMutex);
	while (_connectionQue.empty())
	{
		// 条件变量等待超时时间
		if (cv_status::timeout == _cv.wait_for(lock, chrono::microseconds(_connectionTimeout)))
		{
			// 若果是正常返回,说明真的超时了
			if (_connectionQue.empty())
			{
				LOG("获取空闲连接超时了....获取连接失败!");
				return nullptr;
			}
		}
		else{} // notimeout,再检查一次
	}
	// 这里自定义删除器是因为我们不是要真正删除,而是归还到queue当中
	shared_ptr<Connection> sp(_connectionQue.front(),[&](Connection* pcon) {
		unique_lock<mutex> lock(_queueMutex);
		pcon->refreshAliveTime(); // 刷新一下开始空闲的起始时间
		_connectionQue.push(pcon);
		});
	_connectionQue.pop();
	_cv.notify_all();
	return sp;
}


// 从配置文件加载配置项
bool ConnectionPool::loadConfigFile()
{
	FILE* pf = fopen("mysql.ini", "r");
	if (pf == nullptr)
	{
		LOG("mysql.ini file is not exit");
		return false;
	}
	// 如果文件存在
	while (!feof(pf))
	{
		char line[1024] = { 0 };
		fgets(line, 1024, pf);
		string str = line;
		// 从0开始找=号
		int idx = str.find('=',0);
		if (idx == -1)// 无效配置项
		{
			continue;
		}
		// 会有回车 \n 
		int endidx = str.find('\n', idx);
		string key = str.substr(0, idx);
		string value = str.substr(idx + 1, endidx - idx - 1);
		/*cout << key << " " << value << endl;*/

		if (key == "ip")
		{
			_ip = value;
		}
		else if (key == "port")
		{
			_port = atoi(value.c_str());
		}
		else if (key == "username")
		{
			_username = value;
		}
		else if (key == "password")
		{
			_password = value;
		}
		else if (key == "dbname")
		{
			_dbname = value;
		}
		else if (key == "initSize")
		{
			_initSize = atoi(value.c_str());
		}
		else if (key == "maxSize")
		{
			_maxSize = atoi(value.c_str());
		}
		else if(key == "maxIdleTime")
		{
			_maxIdletime = atoi(value.c_str());
		}
		else if (key == "ConnectionTimeOut")
		{
			_connectionTimeout = atoi(value.c_str());
		}
	}
	return true;
}


性能测试


测试的代码,注意多线程中的Connection需要在一开始先连接,否则后续同时连接是不行的。这是mysql本身的性质决定。

#include"Connection.h"
using namespace std;
#include"ConnectionPool.h"
void SigleWithConnection()
{
	time_t begin = clock();
	for (int i = 0; i < 10000; ++i)
	{
		ConnectionPool* cp = ConnectionPool::getConnectionPool();

		shared_ptr<Connection> sp = cp->getConnection();
		char sql[1024] = { 0 };
		sprintf(sql, "insert into user(name,age,sex) values('%s','%d','%s')",
			"zhangsan", 20, "male");
		sp->update(sql);
	}
	time_t end = clock();
	cout << end - begin << endl;
}
void SigleNoConnection()
{
	time_t begin = clock();
	for (int i = 0; i < 10000; ++i)
	{
		Connection conn;
		char sql[1024] = { 0 };
		sprintf(sql, "insert into user(name,age,sex) values('%s','%d','%s')",
			"zhangsan", 20, "male");
		conn.connect("127.0.0.1", 3307, "root", "123456789", "chat");
		conn.update(sql);
	}
	time_t end = clock();
	cout << end - begin << endl;
}
void MutiNoConnection()
{
	Connection conn;
	conn.connect("127.0.0.1", 3307, "root", "123456789", "chat");

	time_t begin = clock();
	thread t1([&]() {
		for (int i = 0; i < 2500; ++i)
		{
			Connection conn;
			char sql[1024] = { 0 };
			sprintf(sql, "insert into user(name,age,sex) values('%s','%d','%s')",
				"zhangsan", 20, "male");
			conn.connect("127.0.0.1", 3307, "root", "123456789", "chat");
			conn.update(sql);
		}
		});
	thread t2([&]() {
		for (int i = 0; i < 2500; ++i)
		{
			Connection conn;
			char sql[1024] = { 0 };
			sprintf(sql, "insert into user(name,age,sex) values('%s','%d','%s')",
				"zhangsan", 20, "male");
			conn.connect("127.0.0.1", 3307, "root", "123456789", "chat");
			conn.update(sql);
		}
		});
	thread t3([&]() {
		for (int i = 0; i < 2500; ++i)
		{
			Connection conn;
			char sql[1024] = { 0 };
			sprintf(sql, "insert into user(name,age,sex) values('%s','%d','%s')",
				"zhangsan", 20, "male");
			conn.connect("127.0.0.1", 3307, "root", "123456789", "chat");
			conn.update(sql);
		}
		});
	thread t4([&]() {
		for (int i = 0; i < 2500; ++i)
		{
			Connection conn;
			char sql[1024] = { 0 };
			sprintf(sql, "insert into user(name,age,sex) values('%s','%d','%s')",
				"zhangsan", 20, "male");
			conn.connect("127.0.0.1", 3307, "root", "123456789", "chat");
			conn.update(sql);
		}
		});

	t1.join();
	t2.join();
	t3.join();
	t4.join();
	time_t end = clock();
	cout << end - begin << endl;
}

void MutiWithConnection()
{
	time_t begin = clock();
	thread t1([]() {
		for (int i = 0; i < 2500; ++i)
		{
			ConnectionPool* cp = ConnectionPool::getConnectionPool();

			shared_ptr<Connection> sp = cp->getConnection();
			char sql[1024] = { 0 };
			sprintf(sql, "insert into user(name,age,sex) values('%s','%d','%s')",
				"zhangsan", 20, "male");
			if (sp == nullptr)
			{
				cout << "sp is empty" << endl;
				continue;
			}
			sp->update(sql);
		}
		});
	thread t2([]() {
		for (int i = 0; i < 2500; ++i)
		{
			ConnectionPool* cp = ConnectionPool::getConnectionPool();

			shared_ptr<Connection> sp = cp->getConnection();
			char sql[1024] = { 0 };
			sprintf(sql, "insert into user(name,age,sex) values('%s','%d','%s')",
				"zhangsan", 20, "male");
			if (sp == nullptr)
			{
				cout << "sp is empty" << endl;
				continue;
			}
			sp->update(sql);
		}
		});
	thread t3([]() {
		for (int i = 0; i < 2500; ++i)
		{
			ConnectionPool* cp = ConnectionPool::getConnectionPool();

			shared_ptr<Connection> sp = cp->getConnection();
			char sql[1024] = { 0 };
			sprintf(sql, "insert into user(name,age,sex) values('%s','%d','%s')",
				"zhangsan", 20, "male");
			if (sp == nullptr)
			{
				cout << "sp is empty" << endl;
				continue;
			}
			sp->update(sql);
		}
		});
	thread t4([]() {
		for (int i = 0; i < 2500; ++i)
		{
			ConnectionPool* cp = ConnectionPool::getConnectionPool();

			shared_ptr<Connection> sp = cp->getConnection();
			char sql[1024] = { 0 };
			sprintf(sql, "insert into user(name,age,sex) values('%s','%d','%s')",
				"zhangsan", 20, "male");
			if (sp == nullptr)
			{
				cout << "sp is empty" << endl;
				continue;
			}
			sp->update(sql);
		}
		});

	t1.join();
	t2.join();
	t3.join();
	t4.join();
	time_t end = clock();
	cout << end - begin << endl;
}
int main()
{ 
	//MutiNoConnection();
	MutiWithConnection();
	//SigleNoConnection();
	//SigleWithConnection();
	return 0;
}



测试的条件是本地的虚拟机,mysql也是在本地的。可以看到性能基本上是有一倍的提升的。

数据量 未使用连接池花费时间 使用连接池花费时间
1000 单线程:1273ms 四线程:402ms 单线程:606ms 四线程:263ms
5000 单线程:7188ms 四线程:1985ms 单线程:2923ms 四线程:1258ms
10000 单线程:14767ms 四线程:4076ms 单线程:5910ms 四线程:2361ms

难点


这个主线程退出的过早,其余线程用到了主线程的部分数据结构,此时使用是会报mutex destory … 的问题的,并且会有productor线程在条件变量下等待,我这边是加多一个bool isRun的字段,在ConnectionPool用最后唤醒线程来解决的。

总结


代码连接码云:https://gitee.com/wuyi-ljh/test-43—testing/tree/master/connectionpool
参考资料:
C++版mysql数据库连接池

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

【C++实现】 数据库连接池 的相关文章

  • 注销租约抛出 InvalidOperationException

    我有一个使用插件的应用程序 我在另一个应用程序域中加载插件 我使用 RemoteHandle 类http www pocketsilicon com post Things That Make My Life Hell Part 1 App
  • 如何在 .NET Framework 2.0 中模拟“Func<(Of <(TResult>)>) 委托”?

    我尝试使用这个类代码项目文章 http www codeproject com KB threads AsyncVar aspx在 VB NET 和 NET Framework 2 0 中 除了这一行之外 所有内容似乎都可以编译Privat
  • mysql排序和排名语句

    我需要一些 mysql 语句的帮助 我的表 1 有 7 列 表 2 有 8 列 额外的列名为排名 我的语句应该是这样的 从表 1 中选择全部 然后按 用户数 排序 将其插入表 2 中并排名开始 1 2 3 等 table 1 usernam
  • 确保 StreamReader 不会挂起等待数据

    下面的代码读取从 tcp 客户端流读取的所有内容 并且在下一次迭代中它将仅位于 Read 上 我假设正在等待数据 我如何确保它不会在没有任何内容可供读取时返回 我是否必须设置低超时 并在失败时响应异常 或者有更好的办法吗 TcpClient
  • 在 DataView 的 RowFilter 中选择 DISTINCT

    我试图根据与另一个表的关系缩小 DataView 中的行范围 我使用的 RowFilter 如下 dv new DataView myDS myTable id IN SELECT DISTINCT parentID FROM myOthe
  • 复制 std::function 的成本有多高?

    While std function是可移动的 但在某些情况下不可能或不方便 复制它会受到重大处罚吗 它是否可能取决于捕获变量的大小 如果它是使用 lambda 表达式创建的 它依赖于实现吗 std function通常被实现为值语义 小缓
  • C中的malloc内存分配方案

    我在 C 中尝试使用 malloc 发现 malloc 在分配了一些内存后浪费了一些空间 下面是我用来测试 malloc 的一段代码 include
  • 在 C 中匹配二进制模式

    我目前正在开发一个 C 程序 需要解析一些定制的数据结构 幸运的是我知道它们是如何构造的 但是我不确定如何在 C 中实现我的解析器 每个结构的长度都是 32 位 并且每个结构都可以通过其二进制签名来识别 举个例子 有两个我感兴趣的特定结构
  • 使用 Newtonsoft 和 C# 反序列化嵌套 JSON

    我正在尝试解析来自 Rest API 的 Json 响应 我可以获得很好的响应并创建了一些类模型 我正在使用 Newtonsoft 的 Json Net 我的响应中不断收到空值 并且不确定我的模型设置是否正确或缺少某些内容 例如 我想要获取
  • java.io.Serialized 在 C/C++ 中的等价物是什么?

    C C 的等价物是什么java io Serialized https docs oracle com javase 7 docs api java io Serializable html 有对序列化库的引用 用 C 序列化数据结构 ht
  • 将 Word 文档另存为图像

    我正在使用下面的代码将 Word 文档转换为图像文件 但是图片显得太大 内容不适合 有没有办法渲染图片或将图片保存到合适的尺寸 private void btnConvert Click object sender EventArgs e
  • 我可以使用 moq Mock 来模拟类而不是接口吗?

    正在经历https github com Moq moq4 wiki Quickstart https github com Moq moq4 wiki Quickstart 我看到它 Mock 一个接口 我的遗留代码中有一个没有接口的类
  • CMake 无法确定目标的链接器语言

    首先 我查看了this https stackoverflow com questions 11801186 cmake unable to determine linker language with c发帖并找不到解决我的问题的方法 我
  • 使用 C# 读取 Soap 消息

  • 按 Esc 按键关闭 Ajax Modal 弹出窗口

    我已经使用 Ajax 显示了一个面板弹出窗口 我要做的是当用户按 Esc 键时关闭该窗口 这可能吗 如果有人知道这一点或以前做过这一点 请帮助我 Thanks 通过以下链接 您可以通过按退出按钮轻松关闭窗口 http www codepro
  • 不同类型指针之间的减法[重复]

    这个问题在这里已经有答案了 我试图找到两个变量之间的内存距离 具体来说 我需要找到 char 数组和 int 之间的距离 char data 5 int a 0 printf p n p n data 5 a long int distan
  • 方法优化 - C#

    我开发了一种方法 允许我通过参数传入表 字符串 列数组 字符串 和值数组 对象 然后使用这些参数创建参数化查询 虽然它工作得很好 但代码的长度以及多个 for 循环散发出一种代码味道 特别是我觉得我用来在列和值之间插入逗号的方法可以用不同的
  • System.IO.FileNotFoundException:找不到网络路径。在 Windows 7 上使用 DirectoryEntry 对象时出现异常

    我正在尝试使用 DirectoryEntry 对象连接到远程 Windows 7 计算机 这是我的代码 DirectoryEntry obDirEntry new DirectoryEntry WinNT hostName hostName
  • 我的班级应该订阅自己的公共活动吗?

    我正在使用 C 3 0 遵循标准事件模式我有 public event EventHandler
  • 当从finally中抛出异常时,Catch块不会被评估

    出现这个问题的原因是之前在 NET 4 0 中运行的代码在 NET 4 5 中因未处理的异常而失败 部分原因是 try finallys 如果您想了解详细信息 请阅读更多内容微软连接 https connect microsoft com

随机推荐

  • 玩具蛇

    include
  • 数据库SQLite

    数据库SQLite 了解最轻巧的数据库SQLite SQLite 是一款轻型的数据库 占用资源非常低 它的源代码不受版权限制 能够支持Windows Linux Unix等等主流的操作系统 同时能够跟很多程序语言相结合 比如 Tcl C P
  • 29 KVM管理系统资源-调整虚拟CPU绑定关系

    文章目录 29 KVM管理系统资源 调整虚拟CPU绑定关系 29 1 概述 29 2 操作步骤 29 KVM管理系统资源 调整虚拟CPU绑定关系 29 1 概述 把虚拟机的vCPU绑定在物理CPU上 即vCPU只在绑定的物理CPU上调度 在
  • 2-问过 chatgpt 的问题(天马行空想问什么问什么)

    目录 一 信号序列中大部分为 0 时 FFT 运算复杂度的计算 1 当fft运算时 大部分信号点为0的情况下 对fft的运算时间会有影响吗 2 大部分信号点为0的情况下 fft的运算复杂度计算 3 这里的时间复杂度 O N log
  • HTML5语音合成功能

    这篇文章主要介绍了HTML5语音合成功能的实现代码 本文通过实例代码给大家介绍的非常详细 具有一定的参考借鉴价值 需要的朋友参考下吧 可将该代码复制到chrome控制台中体验 let msg new SpeechSynthesisUtter
  • this和super

    this this总是置于当前对象的成员类作区分 this是当前对象的引用 就是说当前用构造函数建的对象是谁 这个this就代表谁 它是一个引用 this 总是指向自己本事 super 子类在重写了父类的方法后 常常还需要使用到父类中被重写
  • do while循环语句的学习以及练习

    今天学的是do while循环语句 先执行循环体 直到条件的表达式为false 与while循环语句的区别 while语句先判断条件 满足时执行循环体 do while语句先执行循环体 满足条件在执行 语法 do 循环体 while 条件
  • kinova-jaco2使用Moveit!控制真实机械臂抓取固定点物体

    kinova jaco2使用Moveit 控制真实机械臂抓取固定点物体 一 机械臂坐标系 坐标系方向 位姿方向 轴的起始点 二 启动机械臂和Moveit 三 实现抓取 python代码 python文件建议直接用python启动 四 遇到的
  • react hook之useMemo

    useMemo的作用 Pass a create function and an array of dependencies useMemo will only recompute the memoized value when one o
  • LogisticRegression(逻辑回归)

    LogisticRegression定义 logistic回归 是一种广义的线性回归分析模型 常用于数据挖掘 疾病自动诊断 经济预测等领域 例如 探讨引发疾病的危险因素 并根据危险因素预测疾病发生的概率等 以胃癌病情分析为例 选择两组人群
  • SVG转为Png

    1 pom中引入maven依赖
  • 自定义实现OAuth2.0 授权码模式

    文章目录 OAuth2 0 授权码模式 实践 依赖知识 术语 授权码流程 认证服务器 拉起请求用户授权页面 用户手动授权 提交授权 生成code 下发Token 第三方应用 收到code并请求Token 访问受保护的资源 项目结构 Tomc
  • 类EMD的“信号分解方法”及MATLAB实现(第四篇)——VMD

    重头戏来了 在以往的应用经验里 VMD方法在众多模态分解方法中可以说是非常好的 从催更力度上看 这个方法也是格外受关注 笔者决定加快进度快一些写完这个方法 十月份了有些同学要开始做毕设 希望这篇文能帮上忙 1 VMD 变分模态分解 的概念
  • poj1338【丑数·DP】

    我记得这道题以前写过 而且是写出来了 DP吧 然后现在想了好久 没想出来 然后考虑一下递推 mdzz 直接就是让之前的这个每次乘以2 3 5就好了嘛 然后每轮取最小 include
  • jquery form表单.serialize()序列化后中文乱码问题原因及解决decodeURIComponent

    jquery form表单 serialize 序列化后中文乱码问题原因及解决 原因 serialize 自动调用了encodeURIComponent方法将数据编码了 解决方法 调用decodeURIComponent XXX true
  • 列表list转树形结构(python/golang/js/php)

    文章目录 1 原数据 2 利用对象内存共享生成嵌套结构 2 1 算法原理 2 2 算法实现 2 2 1 JS 2 2 2 Python 2 2 3 go 2 2 4 php 2 3 运行结果 3 递归 3 1 算法实现 3 1 1 pyth
  • 基于协同过滤算法的书籍推荐 毕业设计-附源码101555

    摘 要 21世纪的今天 随着社会的不断发展与进步 人们对于信息科学化的认识 已由低层次向高层次发展 由原来的感性认识向理性认识提高 管理工作的重要性已逐渐被人们所认识 科学化的管理 使信息存储达到准确 快速 完善 并能提高工作管理效率 促进
  • 微服务restful风格,用Post在服务之间发送请求接收不到参数接收不到问题(@RequestParam和@RequestBody)

    上代码 发送端 接收端 问题 发送端可以接受从前段传过来的数据 但是请求接收端时 接收端可以接收url请求 但是参数传不到接收端 分析 用get和post传输的数据是截然不同的 用get是追加在url之后 直接放在请求头 但是post请求的
  • 体验css:repeat和grid

    文章目录 一 repeat 1 语法 2 auto fill和auto fit 3 专属尺寸 fr auto max content min content 二 grid 1 设置grid布局 2 设置列宽行高 3 设置间距 4 设置分区
  • 【C++实现】 数据库连接池

    文章目录 涉及知识 为什么要弄连接池 功能介绍 成员变量讲解 代码剖析 Connection h Connection cpp ConnectionPool h ConnectionPool cpp 性能测试 难点 总结 涉及知识 MySQ