日志记录函数进入与出来:利用C++的反初始化来记录退出
函数运行记时、调用次数统计等
宏定义
配置里的宏
WIN32;NDEBUG;_WINDOWS;_USRDLL;SMARTPAY_PGLDLL_192787_EXPORTS;ESLOG_RELEASE;HAVE_STRUCT_TIMESPEC;%(PreprocessorDefinitions)
减少代码耦合 关闭日志等
多线程
支持多平台
#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
多编译配置 结合宏定义输出不同版本。
多途径输出
文件、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
通常用于标记那些在程序的执行过程中可能会被意外修改的变量,以防止编译器对其进行优化,确保每次都从内存中读取或写入变量的值。
主要用途包括:
-
硬件寄存器访问:
volatile
常常用于访问硬件寄存器,因为这些寄存器的值可以在程序执行过程中被外部设备更改。通过将变量标记为volatile
,可以确保编译器不会将读写这些寄存器的操作优化掉。volatile int *hwRegister = (volatile int *)0x1234; // 访问硬件寄存器 int value = *hwRegister; // 从寄存器中读取值
-
多线程环境: 在多线程编程中,
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
,值的类型是 FGLCallback
。std::map
是C++标准库中的关联容器,用于存储键-值对,并根据键进行排序。
以下是代码的解释:
std::map<int, FGLCallback>::iterator iter;
-
std::map<int, FGLCallback>
:这部分定义了一个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<FGLDevice>
:这部分定义了一个std::shared_ptr
,它可以管理指向FGLDevice
类型对象的指针。std::shared_ptr
是一种共享所有权的智能指针,允许多个shared_ptr
共享同一个对象,当最后一个shared_ptr
不再需要对象时,对象的内存将自动释放。 -
d
:这是智能指针的名称,你可以使用它来访问FGLDevice
对象,以及对智能指针的其他操作,如复制、释放等。
通常,你可以使用 std::make_shared
函数来创建一个 std::shared_ptr
,并初始化它指向的对象。例如:
#include <memory>
// 创建一个 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)是一种线程状态,它具有以下主要特点和作用:
-
自动资源回收:分离线程在执行结束后,线程库会自动回收其相关资源,包括线程的内存和其他资源,而不需要显式调用
pthread_join
或类似的函数等待线程的结束。这可以减少代码的复杂性,特别是在创建许多临时线程时。 -
不阻塞主线程:在主线程中创建分离线程后,主线程不需要等待分离线程的完成。这允许主线程继续执行其他任务,而不必等待分离线程的结果或等待其完成。
-
线程资源管理:分离线程对于创建临时线程或线程池中的线程非常有用,因为它们可以在不增加主线程的负担的情况下并发执行任务。这可以提高程序的性能和效率。
-
减少资源泄漏风险:当线程是分离状态时,程序员不需要关心线程的资源释放问题,这有助于减少资源泄漏的风险。如果线程是非分离状态(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::mutex>
: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<FGLPipe>(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()); 有回调