从SmartPay dll学到的内容 宏定义 单件模式 迭代 日志记录函数进入与出来

日志记录函数进入与出来:利用C++的反初始化来记录退出

函数运行记时、调用次数统计等

宏定义

配置里的宏

从SmartPay dll学到的内容 宏定义 单件模式 迭代 日志记录函数进入与出来_第1张图片

WIN32;NDEBUG;_WINDOWS;_USRDLL;SMARTPAY_PGLDLL_192787_EXPORTS;ESLOG_RELEASE;HAVE_STRUCT_TIMESPEC;%(PreprocessorDefinitions)

减少代码耦合 关闭日志等

从SmartPay dll学到的内容 宏定义 单件模式 迭代 日志记录函数进入与出来_第2张图片

多线程

支持多平台

#if defined(WIN32)
			//			tcout << szTime;
			//std::cout << strLog << std::endl;
			//			tcout << szLog << std::endl;
#elif defined(linux) || defined(macintosh)
			if (ESLOG_LEVEL_ERROR == m_ulLevel)
			{
				// 红色背景,黄色前景
				std::cerr << '\033' << "[1;33;41m";
				std::cerr << szTime;
				std::cerr << szLog;
				std::cerr << '\033' << "[0m";
				std::cerr << std::endl;
			}
			else
			{
				std::cout << szTime;
				//std::cout << strLog << std::endl;
				std::cout << szLog << std::endl;
			}
#endif

多编译配置 结合宏定义输出不同版本。

从SmartPay dll学到的内容 宏定义 单件模式 迭代 日志记录函数进入与出来_第3张图片

多途径输出

文件、DebugView、控制台等。

OutputDebugStringA(szLog);
std::cout << strLog << std::endl;

https://blog.csdn.net/chenhao0568/article/details/132870969?spm=1001.2014.3001.5501

其它

说了半天,还是没明白!!!

在C++中,volatile是一个关键字,用于告诉编译器不要对带有volatile修饰符的变量进行优化。volatile通常用于标记那些在程序的执行过程中可能会被意外修改的变量,以防止编译器对其进行优化,确保每次都从内存中读取或写入变量的值。

主要用途包括:

  1. 硬件寄存器访问: volatile常常用于访问硬件寄存器,因为这些寄存器的值可以在程序执行过程中被外部设备更改。通过将变量标记为volatile,可以确保编译器不会将读写这些寄存器的操作优化掉。

    volatile int *hwRegister = (volatile int *)0x1234; // 访问硬件寄存器
    int value = *hwRegister; // 从寄存器中读取值
    
  2. 多线程环境: 在多线程编程中,volatile可以用来确保线程之间对共享变量的访问不会被编译器优化掉,从而避免潜在的并发问题。

    volatile int sharedVariable = 0; // 共享变量
    
    // 线程1
    sharedVariable = 1;
    
    // 线程2
    int value = sharedVariable;
    

需要注意的是,volatile只提供了有关变量在内存中的读写行为的信息,但它并不提供线程安全性。在多线程环境中,通常需要使用更强大的同步机制,如互斥锁或原子操作,来确保正确的并发访问。

另外,从C++11开始,C++引入了std::atomic类型,它提供了更强大的原子操作和内存模型,用于处理多线程编程中的共享数据。在许多情况下,std::atomic更适合用来替代volatile来处理多线程问题。

错误消息

您提供的代码片段使用了Windows API中的FormatMessageW函数,该函数用于将系统错误代码转换为相应的错误消息文本。下面是对您提供的代码片段的解释:

FormatMessageW(
    FORMAT_MESSAGE_ALLOCATE_BUFFER |      // 分配缓冲区以存储消息
    FORMAT_MESSAGE_FROM_SYSTEM |          // 从系统中检索错误消息
    FORMAT_MESSAGE_IGNORE_INSERTS,        // 忽略消息中的插入字符串
    NULL,                                 // 所报告的错误代码
    dwErrCode,                            // 错误代码,通常使用GetLastError()获取
    MAKELANGID(LANG_CHINESE, SUBLANG_CHINESE_SIMPLIFIED), // 语言标识,这里是简体中文
    (LPTSTR) &lpMsgBuf,                    // 用于存储错误消息的缓冲区指针
    0,                                    // 缓冲区大小,0表示自动分配
    NULL                                 // 指向插入字符串的指针(在这里不使用)
);

这段代码的目的是将Windows系统中的错误代码(dwErrCode)转换为相应的错误消息文本,并将结果存储在lpMsgBuf所指向的缓冲区中。在这里,您指定了语言标识为简体中文,以获取简体中文的错误消息。

一旦FormatMessageW函数成功执行,lpMsgBuf将包含错误消息的文本,您可以使用它来显示错误信息或进行其他处理。请注意,在使用完lpMsgBuf后,您需要使用LocalFree函数来释放分配的内存,以避免内存泄漏。

这是一种常见的用法,用于在Windows应用程序中将系统错误代码转换为人类可读的错误消息,以便用户或开发人员能够更好地理解发生的问题。

void ShowSystemError(IN u4 dwErrCode) 
{
#if defined(WIN32)
	//Show error message of GetLastError() 
	if (dwErrCode==-1)
	{
		dwErrCode = GetLastError();
	}
	LPVOID lpMsgBuf = NULL; 
	FormatMessageW( 
		FORMAT_MESSAGE_ALLOCATE_BUFFER |
		FORMAT_MESSAGE_FROM_SYSTEM | 
		FORMAT_MESSAGE_IGNORE_INSERTS, 
		NULL, 
		dwErrCode/*GetLastError()*/, 
		MAKELANGID(LANG_CHINESE, SUBLANG_CHINESE_SIMPLIFIED), // Default language 
		(LPTSTR) &lpMsgBuf, 
		0, 
		NULL 
		); 
	WCHAR pbuffer[1024*2] = {0};
	wsprintf(pbuffer,_T("Err[0x%x]:%s"),dwErrCode,(WCHAR*)lpMsgBuf);
	LocalFree( lpMsgBuf ); 
#elif defined(linux)|| defined(macintosh)
	WCHAR pbuffer[1024*2] = {0};
	swprintf(pbuffer, sizeof(pbuffer), L"Err[0x%x]:%s",errno, L"/n");
#endif
	ESLOG_INF(2,_T("%s"),pbuffer);
}

单件模式

MessageLogger& get_msg_logger(void)
{
	static MessageLogger message_logger;
	return message_logger;
}

迭代

这行代码定义了一个名为 iter 的迭代器,该迭代器用于遍历和操作一个名为 std::map 的关联容器,其中键的类型是整数 int,值的类型是 FGLCallbackstd::map 是C++标准库中的关联容器,用于存储键-值对,并根据键进行排序。

以下是代码的解释:

std::map<int, FGLCallback>::iterator iter;
  • std::map:这部分定义了一个std::map 容器,其中键的类型是整数 int,值的类型是 FGLCallback。这表示你可以使用整数作为键,并将 FGLCallback 对象与每个整数相关联。

  • ::iterator:: 操作符用于访问 std::map 的内部类型 iterator,它是一个迭代器类型,用于遍历 std::map 中的元素。

  • iter:这是迭代器的名称,你可以使用它来遍历 std::map 中的元素,例如使用 iter 来访问、修改或删除 std::map 中的键-值对。

要使用这个迭代器,你可以使用循环来遍历 std::map 中的元素,例如:

std::map<int, FGLCallback> myMap;
// 填充 myMap

for (std::map<int, FGLCallback>::iterator iter = myMap.begin(); iter != myMap.end(); ++iter) {
    int key = iter->first;            // 获取键
    FGLCallback value = iter->second; // 获取值

    // 在这里执行操作,如访问或修改键值对
}

这个迭代器用于遍历 std::map 中的所有键-值对,并允许你对它们进行操作。

智能指针

这行代码定义了一个名为 d 的智能指针,它指向类型为 FGLDevice 的对象。具体来说,它是一个 std::shared_ptr,这是C++标准库中的智能指针类型,用于管理动态分配的内存资源,确保资源在不再需要时被正确释放,以避免内存泄漏。

以下是代码的解释:

std::shared_ptr<FGLDevice> d;
  • std::shared_ptr:这部分定义了一个 std::shared_ptr,它可以管理指向 FGLDevice 类型对象的指针。std::shared_ptr 是一种共享所有权的智能指针,允许多个 shared_ptr 共享同一个对象,当最后一个 shared_ptr 不再需要对象时,对象的内存将自动释放。

  • d:这是智能指针的名称,你可以使用它来访问 FGLDevice 对象,以及对智能指针的其他操作,如复制、释放等。

通常,你可以使用 std::make_shared 函数来创建一个 std::shared_ptr,并初始化它指向的对象。例如:

#include 

// 创建一个 std::shared_ptr 指向 FGLDevice 对象
std::shared_ptr<FGLDevice> d = std::make_shared<FGLDevice>();

这将创建一个 std::shared_ptr,指向一个新创建的 FGLDevice 对象,并自动管理对象的生命周期。当不再需要 d 时,它将负责释放关联的 FGLDevice 对象的内存。

分离线程

这段代码是使用 POSIX 线程库(通常称为 pthreads)在多线程环境中创建一个分离线程。下面是对这段代码的解释:

pthread_attr_t attr; // 创建线程属性对象
pthread_attr_init(&attr); // 初始化线程属性对象
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); // 设置线程为分离状态
pthread_t pid; // 创建线程的标识符
pthread_create(&pid, &attr, TransThread::thread_close, (void *) this); // 创建线程
  • pthread_attr_t attr;:这行代码定义了一个线程属性对象 attr,用于指定线程的属性。线程属性包括线程的状态、调度策略等信息。

  • pthread_attr_init(&attr);:这行代码初始化线程属性对象 attr,以确保它处于一个可用的初始状态。

  • pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);:这行代码设置线程属性,将线程状态设置为分离状态,这意味着线程结束后将自动释放其资源,而不需要显式调用 pthread_join 函数来等待线程的结束。

  • pthread_t pid;:这行代码声明一个线程标识符 pid,它将用于标识新创建的线程。

  • pthread_create(&pid, &attr, TransThread::thread_close, (void *) this);:这行代码创建一个新的线程,并将其设置为分离状态。具体解释如下:

    • &pid:传入指向 pid 变量的指针,以便 pthread_create 函数将新线程的标识符存储在 pid 变量中。
    • &attr:传入指向线程属性对象 attr 的指针,以指定新线程的属性。
    • TransThread::thread_close:这是新线程的入口点(线程函数)。在这个例子中,它似乎是一个静态成员函数或类方法,用于执行线程的实际工作。
    • (void *) this:这是传递给线程函数的参数,通常用于传递线程所需的数据。在这里,它似乎将当前对象的指针传递给线程函数。

这段代码的效果是创建一个新线程,该线程在后台运行(分离状态),执行 TransThread::thread_close 函数,并将当前对象的指针传递给该函数。线程属性的设置确保了线程在结束后会自动释放资源。

分离线程(Detached Thread)是一种线程状态,它具有以下主要特点和作用:

  1. 自动资源回收:分离线程在执行结束后,线程库会自动回收其相关资源,包括线程的内存和其他资源,而不需要显式调用 pthread_join 或类似的函数等待线程的结束。这可以减少代码的复杂性,特别是在创建许多临时线程时。

  2. 不阻塞主线程:在主线程中创建分离线程后,主线程不需要等待分离线程的完成。这允许主线程继续执行其他任务,而不必等待分离线程的结果或等待其完成。

  3. 线程资源管理:分离线程对于创建临时线程或线程池中的线程非常有用,因为它们可以在不增加主线程的负担的情况下并发执行任务。这可以提高程序的性能和效率。

  4. 减少资源泄漏风险:当线程是分离状态时,程序员不需要关心线程的资源释放问题,这有助于减少资源泄漏的风险。如果线程是非分离状态(joinable),则需要确保在主线程中正确地调用 pthread_join 函数来等待线程的结束和释放资源。

需要注意的是,分离线程不适合所有情况。在某些情况下,你可能需要创建非分离线程,以等待线程的结果或协调线程之间的操作。选择线程状态(分离或非分离)取决于你的具体需求和应用程序的设计。在需要时,你可以使用线程库提供的函数来设置线程的状态。在POSIX线程库中,使用 pthread_attr_setdetachstate 函数来设置线程的分离状态。

互斥锁

这行代码使用了C++标准库中的std::lock_guard,它是一种用于管理互斥锁(std::mutex)的RAII(资源获取即初始化)类。下面是对这行代码的解释:

std::lock_guard<std::mutex> lk(mutex_);
  • std::lock_guard:这是一个模板类,用于在其作用域内锁定互斥锁,并在作用域结束时自动释放锁。它是一种C++中管理锁的推荐方式,因为它确保在离开作用域时始终释放锁,无论作用域是正常退出还是异常退出。

  • std::lock_guard是一个模板类,需要指定互斥锁的类型作为模板参数。在这里,它指定了std::mutex,表示将使用std::mutex类型的互斥锁。

  • lk:这是std::lock_guard对象的名称,它是一个局部对象,其生命周期与当前作用域相对应。当lk离开其所在的作用域时,它会自动释放关联的互斥锁(mutex_)。

  • mutex_:这是一个名为mutex_的互斥锁,std::lock_guard将会锁定这个互斥锁。

这段代码的作用是在当前作用域中锁定mutex_互斥锁,以确保在锁定期间只有一个线程可以访问受保护的共享资源。一旦lk对象超出其作用域,互斥锁将自动释放,允许其他线程获得锁。这有助于避免多线程中的竞态条件和数据竞争问题。

分析过程

点连接后不断的获取列表 和接收串口数据

在调用每个业务接口时,先把回调等设好,数据放进TransEntity,进到trans里
find_device 设备还在不在 不在就调用demo中的回调callback (response码和response信息)

create_pipe   identity就是串口设备名  MAX_PIPE_SIZE最大使用10个通道

insert_trans_thread  数量不限

find_trans_thread  

thread->run(); 
				thread_run  又创新线程
				find_device
				find_pipe
				pipe->start() 发送开始
				mEntity.getJsonData()
				des3_encrypt 选授权时需要加解密
				pipe->send 发送数据
				ThreadLock::wait(&thread->mMutex, &thread->mCond, timeout * 1000) 等待响应
				
				
				回调if (!d->create_pipe(identity, TransThread::PRO_RECEIVED_CALLBACK))
				    mPipeList.insert(mPipeList.end(), std::make_shared(identity, received));
				     	mReceived = received;
				    		PROTOCOL_RECEIVED mReceived; //pipe received data callback
				    			void FGLPipe::receivedData(FGLMessage *message)
				    				        mReceived(mIdentity, buff, buffLen);  实际的回调
				    				        
				    				        
				    				        void *FGLDevice::thread_received(void *data)调用receivedData
				    				        
				    				        		void FGLDevice::receivedData(const unsigned char *data, unsigned int dataLen)调用了thread_received
				    				        				void FGLProtocol::COMM_RECEIVED_CALLBACK(const char *device,const unsigned char *data, int unsigned dataLen) 调用了        d->receivedData(data, dataLen);
																				void FGLProtocol::COMM_INIT_CALLBACK(int code, const char *data) 调用了        IComm::getInstance().setReceivedListener(FGLProtocol::COMM_RECEIVED_CALLBACK);  看下面
																						void FGLProtocol::initialize(int type, PROTOCOL_RESULT callback)调用了COMM_INIT_CALLBACK
																								void FGLPos::initialize(int type, RESULT_CALLBACK callback)调用了上面
																										void impl_initialize(int comm, RESULT_CALLBACK callback)调用了上面
																											接口void  ConnectDevice(const char *deviceName, FGLCallback callback)调用了上面  
				    	
				   
														setReceivedListener 转到	mCommReceivedCallBack
															unsigned __stdcall Fun1Proc(void* p1)
																	mCommReceivedCallBack(deviceCom.c_str(), pbRecv, nRecvLen);  实际在这里
																			m_Thread.Run(Fun1Proc, 0, mCommReceivedCallBack);  unsigned __stdcall Fun1Proc(void* p1)未使用
				
				void TransThread::PRO_RECEIVED_CALLBACK(const char *identity, const unsigned char *data,unsigned int dataLen) 正常回调
					void TransThread::close(int code, const char *data)
						thread_close 
							result.callback(result.transNo, entity.error_code, entity.error_info.c_str()); 有回调

你可能感兴趣的:(c++,多线程)