YAML
ISO 8601
ROBIN_SET
robin-map:快速hash_map和hash_set - 知乎
robin-map是用c++写的快速hash map 和 hash set,提供快速的插入、查找和删除操作。
Key Features
- 仅头文件
- 速度快
- 高效序列化
- API类似std::unordered_map和std::unordered_set
- MIT license
boost::noncopyable,
class please_dont_make_copies : boost::noncopyable {};
int main() {
please_dont_make_copies d1;
please_dont_make_copies d2(d1);
please_dont_make_copies d3;
d3=d1;
}
这个例子不能通过编译。由于noncopyable的复制构造函数是私有的,因此对d2进行复制构造的尝试会失败。同样,由于noncopyable的赋值操作符也是私有的,因此将d1赋值给d3的尝试也会失败。
UT,IT,ST,UAT
UT,IT,ST,UAT指单元测试,集成测试,系统测试 ,用户接受测试。
CI 是什么?CI 和 CD 有什么区别?
缩略词 CI / CD 具有几个不同的含义。CI/CD 中的“CI”始终指持续集成,它属于开发人员的自动化流程。成功的 CI 意味着应用代码的新更改会定期构建、测试并合并到共享存储库中。该解决方案可以解决在一次开发中有太多应用分支,从而导致相互冲突的问题。
CI/CD 中的“CD”指的是持续交付和/或持续部署,这些相关概念有时会交叉使用。两者都事关管道后续阶段的自动化,但它们有时也会单独使用,用于说明自动化程度
具体而言,CI/CD 可让持续自动化和持续监控贯穿于应用的整个生命周期(从集成和测试阶段,到交付和部署)。这些关联的事务通常被统称为“CI/CD 管道”,由开发和运维团队以敏捷方式协同支持。
_access
win:int _access(char* path,int mode)
gnu:access
头文件<io.h>
功能:确定文件或文件夹的访问权限。如果指定的存取方式有效,则函数返回0,否则函数返回-1。
参数path 是访问文件所在的路径名,mode是访问判断模式,
具体含义如下:
R_OK 只判断是否有读权限
W_OK 只判断是否有写权限
X_OK 判断是否有执行权限
F_OK 只判断是否存在
之前也使用过fopen判断文件是否存在,但_access函数更为方便。
eg:
nFileExit = _access(strnew.c_str(),0);
if(nFileExit == 0)
{
CopyFile(strfilename.c_str(), strnew.c_str(), 0);
}
else
{
}
GetPrivateProfileString
GetPrivateProfileString(lpszSection, lpszKey, lpszDefault, lpszReturnBuffer, cchReturnBuffer, lpszFile)
lpszSection: ini文件中小节的名称,格式[name]
lpszKey: 条目名称
lpszDefault: 当没有读取到对应条目值时,以此值返回, 一般设为NULL
lpszReturnBuffer: 存放所读取的空间
lpszfile: ini 文件绝对名称
#include <cstdlib>
#include <iostream>
#include <Windows.h>
using namespace std;
char IP_addr[128] = {'\0'};
char IP_addr_1[128] = {'\0'};
int main(int argc, char *argv[])
{
cout << g_IP_addr_1<<" here is IP_addr_1"<<endl;
int len=0;
len= GetPrivateProfileStringA("TESTER_SETUP","IP_ADDRESS_1",NULL,IP_addr_1,sizeof(IP_addr),".\\Setup.ini");
//if(0!= len)
cout << g_IP_addr_1<<endl << " here is IP_addr_1"<<endl;
system("PAUSE");
return EXIT_SUCCESS;
}
INI文件:
[TESTER_SETUP]
IP_ADDRESS_1= 192.168.100.254
————————————————
fetch_add()
fetch_sub()
对一个数进行原子操作 +1 -1,一般用在引用计数中、;
内联函数和宏定义
:当编译器发现某段代码在调用一个内联函数时,它不是去调用该函数,而是将该函数的代码,整段插入到当前位置。这样做的好处是省去了调用的过程,加快程序运行速度。(函数的调用过程,由于有前面所说的参数入栈等操作,所以总要多占用一些时间)。这样做的不好处:由于每当代码调用到内联函数,就需要在调用处直接插入一段该函数的代码,所以程序的体积将增大
1、内联函数在可读性方面与函数是相同的,而在编译时是将函数直接嵌入调用程序的主体,省去了调用/返回指令,这样在运行时速度更快。
2、内联函数可以调试,而宏定义是不可以调试的。
内联函数与宏本质上是两个不同的概念如果程序编写者对于既要求快速,又要求可读的情况下,则应该将函数冠以inline。下面详细介绍一下探讨一下内联函数与宏定义
a、内联函数编译时要做类型检查,而后在将代码在函数调用点展开;而宏定义是在代码处不做任何检查的简单替换,从这点来看,内联函数更为安全。
b、inline函数是函数,但在编译时不单独产生代码,而是将有关代码嵌入到调用处;而宏定义不是一个函数只是在编译预处理阶段将有关字符串替换成宏体
————————————————
与C++11多线程相关的头文件
C++11 新标准中引入了四个头文件来支持多线程编程,他们分别是< atomic> ,< thread>,< mutex>,< condition_variable>和< future>。
线程 使用线程类,更加优秀的跨平台
互斥量和锁 同样优秀的跨平台。
std:: thread
C++——多线程编程(一)std::thread_linyyy的博客-CSDN博客
std::ref函数_Ch_zaqdt的博客-CSDN博客_std::ref的作用
C++11 的std::ref函数就是为了解决在线程的创建中等过程的值拷贝问题,下面将会用一个线程的创建来展示ref函数的作用。
因为fun函数中的对象和主函数中的对象所在的内存地址不同,所以如果我们想在fun函数中对这个对象进行值的修改的时候在main中就不会起作用,这就失去了引用的一个作用
可以看到参数传递的过程中没有调用拷贝构造函数,而且内存地址相同,更改的值也达到了我们想要的效果,但是为了保证线程的安全,这样的方式不可以用detach使主线程与子线程分开。
join()方法的作用就是让主线程等待子线程执行结束之后再运行主线程
mutable 有什么用?如果我想在常成员函数内部,修改对象的某个成员变量时,怎么办?这时,就需要 引入 mutable,将该对象的成员变量定义为 mutable 变量,但是,我们会把定义为mutable的这个变量,看做不属于对象的状态。这样,即使在常成员函数内部,mutable变量也可以被修改,与此同时,常成员函数内部,对于对象的其他成员变量(即对象的内部状态)还是不能修改的
————————————————
mutex和recursive_mutex
void lock()
mutex使用锁有三种情况,
1、任何线程都没有使用mutex, 则调用的线程使用mutex
2、有其他线程使用mutex,当前调用的线程会阻塞,直到其他使用锁的线程锁释放
3、如果mutex被同一线程调用,会产生死锁
recursive_mutex和mutex类似,如果没有线程使用,则上锁
如果有线程使用,则阻塞
如果同一线程使用mutex,则添加新一层mutex,对应的释放时,需要多一次unlock()
std::this_thread::sleep_for
阻塞当前线程执行,至少经过指定的 sleep_duration
。
此函数可能阻塞长于 sleep_duration
,因为调度或资源争议延迟。
标准库建议用稳定时钟度量时长。若实现用系统时间代替,则等待时间亦可能对时钟调节敏感
lock_guard
相比于mutex功能,lock_guard具有创建时加锁,析构时解锁的功能,类似于智能指针,为了防止在线程使用mutex加锁后异常退出导致死锁的问题,建议使用lock_guard代替mutex。下面利用代码演示功能:
lock_guard具有两种构造方法:
lock_guard(mutex& m)
lock_guard(mutex& m, adopt_lock)
其中mutex& m
是互斥量,参数adopt_lock
表示假定调用线程已经获得互斥体所有权并对其进行管理了,因为构造时候会自动枷锁,所以adopt_lock其实就是告诉这个锁已经提前加过了。
想起了于哥写的自动的成员变量锁
#include <iostream>
#include <mutex>
#include <vector>
#include <string>
#include <ctime>
#include <thread>
using namespace std;
// 时间模拟消息
string mock_msg()
{
char buff[30] = { 0 };
static int i = 100000;
sprintf_s(buff, "%d", i--);
return buff;
}
class CMutexTest
{
public:
void recv_msg(); //接收消息
void read_msg(); //处理消息
private:
vector<string> msg_queue; //消息队列
mutex m_mutex; //互斥量
};
// 模拟接收消息
void CMutexTest::recv_msg()
{
while (true)
{
string msg = mock_msg();
cout << "recv the msg " << msg << endl;
// 使用"{ }"限定lock_guard作用域
{
lock_guard<mutex> mylockguard(m_mutex);
// 消息添加到队列
msg_queue.push_back(msg);
}
this_thread::sleep_for(chrono::milliseconds(10));//方便观察数据
}
}
// 模拟读取处理
void CMutexTest::read_msg()
{
while (true)
{
// 已经加锁
m_mutex.lock();
// 传递所有权给lock_guard,并传入adopt_lock表示已获得所有权
lock_guard<mutex> mylockguard(m_mutex, std::adopt_lock);
if (!msg_queue.empty())
{
// 处理消息并移除
string msg = msg_queue.front();
cout << "read the msg " << msg << endl;
msg_queue.erase(msg_queue.begin());
}
this_thread::sleep_for(chrono::milliseconds(15));//方便观察数据
}
}
int main()
{
CMutexTest my_test;
thread recv_thread(&CMutexTest::recv_msg, &my_test); //接收线程
thread read_thread(&CMutexTest::read_msg, &my_test); //处理线程
recv_thread.join();
read_thread.join();
}
unique_lock取代lock_guard
unique_lock是个类模板,工作中,一般lock_guard(推荐使用);lock_guard取代了mutex的lock()和unlock();
unique_lock比lock_guard灵活很多,效率上差一点,内存占用多一点。
C++11多线程 unique_lock详解_u012507022的博客-CSDN博客_c++ unique_lock
void inMsgRecvQueue()
{
for (int i = 0; i < 10000; i++)
{
cout << "inMsgRecvQueue()执行,插入一个元素" << i << endl;
std::unique_lock<std::mutex> sbguard(my_mutex, std::defer_lock);//没有加锁的my_mutex
sbguard.lock();//咱们不用自己unlock
//处理共享代码
//因为有一些非共享代码要处理
sbguard.unlock();
//处理非共享代码要处理。。。
sbguard.lock();
//处理共享代码
msgRecvQueue.push_back(i);
//...
//其他处理代码
sbguard.unlock();//画蛇添足,但也可以
}
}
unique_lock所有权的传递
std::unique_lock<std::mutex> sbguard(my_mutex);//所有权概念
sbguard拥有my_mutex的所有权;sbguard可以把自己对mutex(my_mutex)的所有权转移给其他的unique_lock对象;
所以unique_lock对象这个mutex的所有权是可以转移,但是不能复制。
方法1 :std::move()
方法2:return std:: unique_lock<std::mutex> 代码如下:
std::unique_lock<std::mutex> sbguard2(std::move(sbguard));//移动语义,现在先当与sbguard2与my_mutex绑定到一起了
//现在sbguard1指向空,sbguard2指向了my_mutex
std::unique_lock<std::mutex> rtn_unique_lock()
{
std::unique_lock<std::mutex> tmpguard(my_mutex);
return tmpguard;//从函数中返回一个局部的unique_lock对象是可以的。三章十四节讲解过移动构造函数。
//返回这种举报对象tmpguard会导致系统生成临时unique_lock对象,并调用unique_lock的移动构造函数
}
void inMsgRecvQueue()
{
for (int i = 0; i < 10000; i++)
{
std::unique_lock<std::mutex> sbguard1 = rtn_unique_lock();
msgRecvQueue.push_back(i);
}
}
总结一下,就是lock_guard 配合 adopt_lock使用,unique_lock配合 defer_lock使用。在两种情况都能使用时,推荐使用lock_guard,因为它更快并且使用的内存空间更少。unique_lock支持所有权的传递,所以更加灵活。
————————————————
版权声明:本文为CSDN博主「u012507022」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/u012507022/article/details/85909567
condition_variable
当 std::condition_variable
对象的某个wait
函数被调用的时候,它使用 std::unique_lock
(通过 std::mutex
) 来锁住当前线程。当前线程会一直被阻塞,直到另外一个线程在相同的 std::condition_variable
对象上调用了 notification
函数来唤醒当前线程。
std::condition_variable
对象通常使用 std::unique_lock
来等待,如果需要使用另外的 lockable
类型,可以使用std::condition_variable_any
类,本文后面会讲到 std::condition_variable_any
的用法。
std::condition_variable::notify_one() 介绍
唤醒某个等待(wait
)线程。如果当前没有等待线程,则该函数什么也不做,如果同时存在多个等待线程,则唤醒某个线程是不确定的(unspecified)
。
std::condition_variable_any 介绍
与 std::condition_variable
类似,只不过std::condition_variable_any
的 wait
函数可以接受任何 lockable
参数,而 std::condition_variable
只能接受 std::unique_lock
类型的参数,除此以外,和std::condition_variable
几乎完全一样。
临时线程
C++11开始支持多线程编程,之前多线程编程都需要系统的支持,在不同的系统下创建线程需要不同的API如pthread_create(),Createthread(),beginthread()等,使用起来都比较复杂,C++11提供了新头文件<thread>、<mutex>、<atomic>、<future>等用于支持多线程
int main()
{
std::vector<std::thread> threads;
for(int i = 0; i < 5; ++i){
threads.push_back(std::thread([](){
std::cout << "Hello from lamda thread " << std::this_thread::get_id() << std::endl;
}));
}
C++11支持Lambda表达式,因此一个新线程的回调函数也可以是有一个Lambda表达式的形式,但是注意如果使用Lambda表达式最好不要使用引用的方式,应该使用值传递的方式来访问数据,在多线程中使用引用容易造成混乱。如果用了引用传参,最好把线程detach掉。
总结
(a)如果传递int这种简单类型,建议都是值传递,不要用引用,防止节外生枝
(b)如果传递类对象,避免隐式类型转换。全部都是创建线程这一行就创建出临时对象,然后在函数参数里,用引用来接,否则系统还会构建出一个对象
(c)终极结论:建议不使用detach(),只使用join(),这样就不存在局部变量失效导致线程对内存的非法引用问题
shared_ptr<thread> thrd(new thread([this](){ std::this_thread::sleep_for(std::chrono::seconds(2);
print("dosomething"))}))
————————————————
版权声明:本文为CSDN博主「Scarlett2025」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/Scarlett2025/article/details/123011038
DllMain简介
跟exe有个main或者WinMain入口函数一样,DLL也有一个入口函数,就是DllMain。以“DllMain”为关键字,来看看MSDN帮助文档怎么介绍这个函数的。
The DllMain function is an optional method of entry into a dynamic-link library (DLL) 。(简要翻译:对于一个Dll模块,DllMain函数是可选的。)这句话很重要,很多初学者可能都认为一个动态链接库肯定要有DllMain函数。其实不然,像很多仅仅包含资源信息的DLL是没有DllMain函数的。
1.2 何时调用DllMain
系统是在什么时候调用DllMain函数的呢?静态链接时,或动态链接时调用LoadLibrary和FreeLibrary都会调用DllMain函数。DllMain的第三个参数fdwReason指明了系统调用Dll的原因,它可能是DLL_PROCESS_ATTACH、DLL_PROCESS_DETACH、DLL_THREAD_ATTACH和DLL_THREAD_DETACH。以下从这四种情况来分析系统何时调用了DllMain。
1.2.1 DLL_PROCESS_ATTACH
大家都知道,一个程序要调用Dll里的函数,首先要先把DLL文件映射到进程的地址空间。要把一个DLL文件映射到进程的地址空间,有两种方法:静态链接和动态链接的LoadLibrary或者LoadLibraryEx。
当一个DLL文件被映射到进程的地址空间时,系统调用该DLL的DllMain函数,传递的fdwReason参数为DLL_PROCESS_ATTACH。这种调用只会发生在第一次映射时。如果同一个 进程后来为已经映射进来的DLL再次调用LoadLibrary或者LoadLibraryEx,操作系统只会增加DLL的使用次数,它不会再用DLL_PROCESS_ATTACH调用DLL的DllMain函数。不同进程用LoadLibrary同一个DLL时,每个进程的第一次映射都会用DLL_PROCESS_ATTACH调用DLL的DllMain函数。
可参考DllMainTest的DLL_PROCESS_ATTACH_Test函数
————————————————
版权声明:本文为CSDN博主「许振坪」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/benkaoya/article/details/2504781