|
@@ -51,8 +51,8 @@ void CPosPrinterWorker::Stop()
|
|
|
|
|
|
|
|
void CPosPrinterWorker::RefreshShouyinChannels()
|
|
void CPosPrinterWorker::RefreshShouyinChannels()
|
|
|
{
|
|
{
|
|
|
- //设置变更后重建通道,若打印线程正在等待最慢端口,最多等一个超时周期
|
|
|
|
|
- std::lock_guard<std::mutex> lock(m_shouyin_channels_mutex);
|
|
|
|
|
|
|
+ //设置变更后重建通道。容器锁在 StartShouyinChannels/StopShouyinChannels 内部加,
|
|
|
|
|
+ //这里不能持锁(否则与内部加锁自死锁,且会把 join 拖进临界区)
|
|
|
StartShouyinChannels();
|
|
StartShouyinChannels();
|
|
|
}
|
|
}
|
|
|
|
|
|
|
@@ -128,6 +128,9 @@ void CPosPrinterWorker::StartShouyinChannels()
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
//每个端口一个通道,打开失败的端口也保留通道,等待热插拔重试
|
|
//每个端口一个通道,打开失败的端口也保留通道,等待热插拔重试
|
|
|
|
|
+ //先建到局部,最后一次性挂进 m_shouyin_channels(加容器锁),避免“半成品”被发送线程看到
|
|
|
|
|
+ std::vector<std::shared_ptr<CPortSendChannel>> new_channels;
|
|
|
|
|
+
|
|
|
for (std::vector<std::pair<std::wstring, PrinterPortKind>>::iterator it = ports.begin(); it != ports.end(); it++)
|
|
for (std::vector<std::pair<std::wstring, PrinterPortKind>>::iterator it = ports.begin(); it != ports.end(); it++)
|
|
|
{
|
|
{
|
|
|
std::shared_ptr<CPortSendChannel> ch = std::make_shared<CPortSendChannel>();
|
|
std::shared_ptr<CPortSendChannel> ch = std::make_shared<CPortSendChannel>();
|
|
@@ -137,27 +140,52 @@ void CPosPrinterWorker::StartShouyinChannels()
|
|
|
OpenChannelHandle(ch);
|
|
OpenChannelHandle(ch);
|
|
|
|
|
|
|
|
ch->worker = std::thread(&CPosPrinterWorker::ChannelWorker, this, ch);
|
|
ch->worker = std::thread(&CPosPrinterWorker::ChannelWorker, this, ch);
|
|
|
- m_shouyin_channels.push_back(ch);
|
|
|
|
|
|
|
+ new_channels.push_back(ch);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ {
|
|
|
|
|
+ std::lock_guard<std::mutex> lock(m_shouyin_channels_mutex);
|
|
|
|
|
+ for (std::vector<std::shared_ptr<CPortSendChannel>>::iterator it = new_channels.begin(); it != new_channels.end(); it++)
|
|
|
|
|
+ {
|
|
|
|
|
+ m_shouyin_channels.push_back(*it);
|
|
|
|
|
+ }
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
void CPosPrinterWorker::StopShouyinChannels()
|
|
void CPosPrinterWorker::StopShouyinChannels()
|
|
|
{
|
|
{
|
|
|
- for (std::vector<std::shared_ptr<CPortSendChannel>>::iterator it = m_shouyin_channels.begin(); it != m_shouyin_channels.end(); it++)
|
|
|
|
|
|
|
+ //先把通道整体摘出来:join 绝不能发生在持有 m_shouyin_channels_mutex 的时候,
|
|
|
|
|
+ //否则发送线程(SendDataToShouyinPirnter 持该锁)会被长时间挡住
|
|
|
|
|
+ std::vector<std::shared_ptr<CPortSendChannel>> channels;
|
|
|
|
|
+ {
|
|
|
|
|
+ std::lock_guard<std::mutex> lock(m_shouyin_channels_mutex);
|
|
|
|
|
+ channels.swap(m_shouyin_channels);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ //通知所有通道退出(stop 与 worker 的读取用 ch->mtx 串行)
|
|
|
|
|
+ for (std::vector<std::shared_ptr<CPortSendChannel>>::iterator it = channels.begin(); it != channels.end(); it++)
|
|
|
|
|
+ {
|
|
|
|
|
+ std::lock_guard<std::mutex> lock((*it)->mtx);
|
|
|
|
|
+ (*it)->stop = true;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ //等线程真正退出:worker 是短睡轮询,最多一个 Sleep 周期就返回
|
|
|
|
|
+ for (std::vector<std::shared_ptr<CPortSendChannel>>::iterator it = channels.begin(); it != channels.end(); it++)
|
|
|
{
|
|
{
|
|
|
std::shared_ptr<CPortSendChannel> ch = *it;
|
|
std::shared_ptr<CPortSendChannel> ch = *it;
|
|
|
- {
|
|
|
|
|
- std::lock_guard<std::mutex> lock(ch->mtx);
|
|
|
|
|
- ch->stop = true;
|
|
|
|
|
- }
|
|
|
|
|
- ch->cv.notify_one();
|
|
|
|
|
if (ch->worker.joinable())
|
|
if (ch->worker.joinable())
|
|
|
{
|
|
{
|
|
|
|
|
+ HANDLE hThread = ch->worker.native_handle();
|
|
|
|
|
+ while (::WaitForSingleObject(hThread, 10) == WAIT_TIMEOUT)
|
|
|
|
|
+ {
|
|
|
|
|
+ //尽力取消该线程正在进行的同步WriteFile;若驱动不支持取消,仍需等待线程返回
|
|
|
|
|
+ ::CancelSynchronousIo(hThread);
|
|
|
|
|
+ }
|
|
|
ch->worker.join();
|
|
ch->worker.join();
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- m_shouyin_channels.clear();
|
|
|
|
|
|
|
+ //channels 出作用域:shared_ptr 归零,端口句柄已由 worker 自身收尾关闭
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
void CPosPrinterWorker::ChannelWorker(std::shared_ptr<CPortSendChannel> ch)
|
|
void CPosPrinterWorker::ChannelWorker(std::shared_ptr<CPortSendChannel> ch)
|
|
@@ -166,14 +194,28 @@ void CPosPrinterWorker::ChannelWorker(std::shared_ptr<CPortSendChannel> ch)
|
|
|
{
|
|
{
|
|
|
std::string data;
|
|
std::string data;
|
|
|
{
|
|
{
|
|
|
- std::unique_lock<std::mutex> lock(ch->mtx);
|
|
|
|
|
- ch->cv.wait(lock, [&ch] { return ch->stop || !ch->queue.empty(); });
|
|
|
|
|
- if (ch->stop)
|
|
|
|
|
|
|
+ //非阻塞取任务:抢不到锁就下一轮再看,绝不在锁上/条件变量上等待
|
|
|
|
|
+ std::unique_lock<std::mutex> lock(ch->mtx, std::try_to_lock);
|
|
|
|
|
+ if (lock.owns_lock())
|
|
|
{
|
|
{
|
|
|
- break;
|
|
|
|
|
|
|
+ if (ch->stop)
|
|
|
|
|
+ {
|
|
|
|
|
+ break;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ if (!ch->queue.empty())
|
|
|
|
|
+ {
|
|
|
|
|
+ data = ch->queue.front();
|
|
|
|
|
+ ch->queue.pop();
|
|
|
|
|
+ }
|
|
|
}
|
|
}
|
|
|
- data = ch->queue.front();
|
|
|
|
|
- ch->queue.pop();
|
|
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ if (data.empty())
|
|
|
|
|
+ {
|
|
|
|
|
+ //没有数据(或暂时拿不到锁):短睡后再来,不阻塞其他端口
|
|
|
|
|
+ ::Sleep(20);
|
|
|
|
|
+ continue;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
//句柄未打开(设备拔掉或被占用),尝试重新打开
|
|
//句柄未打开(设备拔掉或被占用),尝试重新打开
|
|
@@ -186,9 +228,16 @@ void CPosPrinterWorker::ChannelWorker(std::shared_ptr<CPortSendChannel> ch)
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- //慢端口超时只卡本通道的线程,不影响其他端口
|
|
|
|
|
|
|
+ //同步写出:数据写进驱动缓冲即返回,不做超时等待
|
|
|
DWORD dwWrite = 0;
|
|
DWORD dwWrite = 0;
|
|
|
- bool ret = WriteWithTimeout(ch->hPort, data.c_str(), (DWORD)data.length(), 5000, dwWrite);
|
|
|
|
|
|
|
+ BOOL ret = ::WriteFile(ch->hPort, data.c_str(), (DWORD)data.length(), &dwWrite, NULL);
|
|
|
|
|
+ {
|
|
|
|
|
+ std::lock_guard<std::mutex> lock(ch->mtx);
|
|
|
|
|
+ if (ch->stop)
|
|
|
|
|
+ {
|
|
|
|
|
+ break;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
if (!ret)
|
|
if (!ret)
|
|
|
{
|
|
{
|
|
@@ -200,8 +249,23 @@ void CPosPrinterWorker::ChannelWorker(std::shared_ptr<CPortSendChannel> ch)
|
|
|
|
|
|
|
|
if (OpenChannelHandle(ch))
|
|
if (OpenChannelHandle(ch))
|
|
|
{
|
|
{
|
|
|
|
|
+ {
|
|
|
|
|
+ std::lock_guard<std::mutex> lock(ch->mtx);
|
|
|
|
|
+ if (ch->stop)
|
|
|
|
|
+ {
|
|
|
|
|
+ break;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
dwWrite = 0;
|
|
dwWrite = 0;
|
|
|
- ret = WriteWithTimeout(ch->hPort, data.c_str(), (DWORD)data.length(), 5000, dwWrite);
|
|
|
|
|
|
|
+ ret = ::WriteFile(ch->hPort, data.c_str(), (DWORD)data.length(), &dwWrite, NULL);
|
|
|
|
|
+ {
|
|
|
|
|
+ std::lock_guard<std::mutex> lock(ch->mtx);
|
|
|
|
|
+ if (ch->stop)
|
|
|
|
|
+ {
|
|
|
|
|
+ break;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
@@ -231,7 +295,8 @@ bool CPosPrinterWorker::OpenChannelHandle(std::shared_ptr<CPortSendChannel> ch)
|
|
|
}
|
|
}
|
|
|
else
|
|
else
|
|
|
{
|
|
{
|
|
|
- hPort = CreateFile(ch->portPath.c_str(), GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED, NULL);
|
|
|
|
|
|
|
+ //同步句柄(不带 FILE_FLAG_OVERLAPPED),WriteFile 写进驱动缓冲即返回,无需超时等待
|
|
|
|
|
+ hPort = CreateFile(ch->portPath.c_str(), GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
if (hPort == INVALID_HANDLE_VALUE || hPort == NULL)
|
|
if (hPort == INVALID_HANDLE_VALUE || hPort == NULL)
|
|
@@ -263,7 +328,8 @@ bool CPosPrinterWorker::OpenChannelHandle(std::shared_ptr<CPortSendChannel> ch)
|
|
|
|
|
|
|
|
HANDLE CPosPrinterWorker::OpenComPort(const std::wstring& com_path)
|
|
HANDLE CPosPrinterWorker::OpenComPort(const std::wstring& com_path)
|
|
|
{
|
|
{
|
|
|
- HANDLE hPort = CreateFile(com_path.c_str(), GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED, NULL);
|
|
|
|
|
|
|
+ //同步句柄(不带 FILE_FLAG_OVERLAPPED),WriteFile 写进驱动缓冲即返回,无需超时等待
|
|
|
|
|
+ HANDLE hPort = CreateFile(com_path.c_str(), GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
|
|
|
|
|
|
|
|
if (hPort == INVALID_HANDLE_VALUE)
|
|
if (hPort == INVALID_HANDLE_VALUE)
|
|
|
{
|
|
{
|
|
@@ -285,7 +351,7 @@ HANDLE CPosPrinterWorker::OpenComPort(const std::wstring& com_path)
|
|
|
LOG_INFO(com_path.c_str() << " success, handle:" << hPort);
|
|
LOG_INFO(com_path.c_str() << " success, handle:" << hPort);
|
|
|
|
|
|
|
|
//佳博的串口打印机,默认是这个波特率
|
|
//佳博的串口打印机,默认是这个波特率
|
|
|
- DCB dcb;
|
|
|
|
|
|
|
+ DCB dcb = {};
|
|
|
dcb.DCBlength = sizeof(dcb);
|
|
dcb.DCBlength = sizeof(dcb);
|
|
|
GetCommState(hPort, &dcb);
|
|
GetCommState(hPort, &dcb);
|
|
|
dcb.BaudRate = 19200;
|
|
dcb.BaudRate = 19200;
|
|
@@ -298,12 +364,13 @@ HANDLE CPosPrinterWorker::OpenComPort(const std::wstring& com_path)
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
//设定通讯端口超时参数
|
|
//设定通讯端口超时参数
|
|
|
|
|
+ //写超时固定总量1秒(不按字节累加),避免大数据量时WriteFile长时间阻塞
|
|
|
COMMTIMEOUTS tmouts;
|
|
COMMTIMEOUTS tmouts;
|
|
|
tmouts.ReadIntervalTimeout = 100;
|
|
tmouts.ReadIntervalTimeout = 100;
|
|
|
tmouts.ReadTotalTimeoutMultiplier = 100;
|
|
tmouts.ReadTotalTimeoutMultiplier = 100;
|
|
|
tmouts.ReadTotalTimeoutConstant = 100;
|
|
tmouts.ReadTotalTimeoutConstant = 100;
|
|
|
- tmouts.WriteTotalTimeoutConstant = 100;
|
|
|
|
|
- tmouts.WriteTotalTimeoutMultiplier = 100;
|
|
|
|
|
|
|
+ tmouts.WriteTotalTimeoutConstant = 1000;
|
|
|
|
|
+ tmouts.WriteTotalTimeoutMultiplier = 0;
|
|
|
SetCommTimeouts(hPort, &tmouts);
|
|
SetCommTimeouts(hPort, &tmouts);
|
|
|
|
|
|
|
|
//设置端口缓冲
|
|
//设置端口缓冲
|
|
@@ -479,11 +546,8 @@ void CPosPrinterWorker::SendDataToShouyinPirnter(std::string data)
|
|
|
for (std::vector<std::shared_ptr<CPortSendChannel>>::iterator it = m_shouyin_channels.begin(); it != m_shouyin_channels.end(); it++)
|
|
for (std::vector<std::shared_ptr<CPortSendChannel>>::iterator it = m_shouyin_channels.begin(); it != m_shouyin_channels.end(); it++)
|
|
|
{
|
|
{
|
|
|
std::shared_ptr<CPortSendChannel> ch = *it;
|
|
std::shared_ptr<CPortSendChannel> ch = *it;
|
|
|
- {
|
|
|
|
|
- std::lock_guard<std::mutex> ch_lock(ch->mtx);
|
|
|
|
|
- ch->queue.push(data);
|
|
|
|
|
- }
|
|
|
|
|
- ch->cv.notify_one();
|
|
|
|
|
|
|
+ std::lock_guard<std::mutex> ch_lock(ch->mtx);
|
|
|
|
|
+ ch->queue.push(data);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
return;
|
|
return;
|
|
@@ -984,7 +1048,12 @@ bool CPosPrinterWorker::WriteWithTimeout(HANDLE hPort, const void* buffer, DWORD
|
|
|
}
|
|
}
|
|
|
else
|
|
else
|
|
|
{
|
|
{
|
|
|
|
|
+ //超时:请求取消后必须等这次 I/O 真正收尾再释放 OVERLAPPED,
|
|
|
|
|
+ //否则完成状态会写回已经出栈的栈帧,可能写坏上层数据
|
|
|
CancelIoEx(hPort, &ov);
|
|
CancelIoEx(hPort, &ov);
|
|
|
|
|
+ WaitForSingleObject(ov.hEvent, INFINITE);
|
|
|
|
|
+ DWORD dwDummy = 0;
|
|
|
|
|
+ GetOverlappedResult(hPort, &ov, &dwDummy, FALSE);
|
|
|
ret = FALSE;
|
|
ret = FALSE;
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
@@ -1026,7 +1095,11 @@ bool CPosPrinterWorker::ReadWithTimeout(HANDLE hPort, void* buffer, DWORD buffer
|
|
|
}
|
|
}
|
|
|
else
|
|
else
|
|
|
{
|
|
{
|
|
|
|
|
+ //超时:请求取消后必须等这次 I/O 真正收尾再释放 OVERLAPPED(同上)
|
|
|
CancelIoEx(hPort, &ov);
|
|
CancelIoEx(hPort, &ov);
|
|
|
|
|
+ WaitForSingleObject(ov.hEvent, INFINITE);
|
|
|
|
|
+ DWORD dwDummy = 0;
|
|
|
|
|
+ GetOverlappedResult(hPort, &ov, &dwDummy, FALSE);
|
|
|
ret = FALSE;
|
|
ret = FALSE;
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|