我正在尝试将ZMQ集成到现有的Windows应用程序中,该应用程序严重依赖于MFC套接字(CASyncSocket)。
我有一个CWinThread派生的UI线程(没有GUI),它使用CAsyncSocket异步与服务器通信。我想添加一个ZMQ inproc通信线来处理从服务器接收的数据(基于REQ / REP)与应用程序中的其他线程的通信。
使用CAsyncSocket时,每当要接收的套接字上有新数据时,MFC框架就会调用OnReceive方法(这可能是对那里的硬核MFC专家的过度简化)。
ZMQ中是否有这样的机制?或者我是否必须添加一个额外的专用WorkerThread,UI线程启动以处理我与应用程序其余部分的ZMQ通信?两个管道上的流量都很小,所以我真的不想创建2个单独的线程,如果我可以使用1。
注意,我已经掌握了基础知识,我只是遇到了同步问题。如果我使用阻塞recv / send与ZMQ,它会破坏我的CAsycSocket,因为Windows消息永远不会被线程处理,导致有时永远不会从ZMQ应该传递的服务器获取数据。但是如果我使用非阻塞的ZMQ调用,那么线程经常最终处于空闲状态,因为它不知道要读取ZMQ套接字。
答案 0 :(得分:1)
最终,答案是不。当数据到达您可以链接到的ZeroMQ时,没有当前的回调/通知。我也找不到任何增加此功能的分支。
当使用MFC套接字框架在单个线程中提供的传统OnReceive调用并且添加第二个线程专用于ZMQ时,我无法使ZMQ工作,从而打败了使用它的整个目的(它用于线程同步) )。
我的实现最终会丢弃MFC套接字并将ZMQ用于我的inproc服务器(用于与其他线程通信)以及我的TCP(非ZMQ)服务器连接并使用阻塞轮询调用(zmq_poll())在OnIdle()方法中(每次返回1以创建一个忙循环)。阻止民意调查
BOOL CMyThreaClass::OnIdle(LONG lCount)
{
UNREFERENCED_PARAMETER(lCount);
zmq_pollitem_t items [] = {
{ m_pZMQInprocServer, 0, ZMQ_POLLIN, 0 },
{ m_pZMQTCPSocket, 0, ZMQ_POLLIN, 0 }
};
const int iZMQInfiniteTimeout(-1);
iResult = zmq_poll(&items[0], sizeof(items) / sizeof(items[0]), iZMQInfiniteTimeout);
TRACE("zmq_poll result: %d\n", iResult);
if (items[0].revents & ZMQ_POLLIN)
{
sMyStruct sMessage;
iResult = zmq_recv(m_pZMQInprocServer, &sMessage, sizeof(sMessage), ZMQ_DONTWAIT); // don't block (the zmq_poll blocks for us)
TRACE("inproc recv result: %d\n", iResult);
// Process inproc messages
iResult = zmq_send(pZMQInprocServer, &sMessage, sizeof(sMessage), ZMQ_NULL); // block
TRACE("inproc send result: %d\n", iResult);
}
if (items[1].revents & ZMQ_POLLIN)
{
// there will be an ZMQ_IDENTITY identifier on the beginning of the socket buffer, read it off first
uint8_t id [256];
size_t id_size = 256;
iResult = zmq_getsockopt(m_pZMQTCPSocket, ZMQ_IDENTITY, id, &id_size);
TRACE("getsockopt poll result %d:id %d\n", iResult, id);
iResult = zmq_recv(m_pZMQTCPSocket, &id, id_Size, ZMQ_DONTWAIT); // don't block
// now get our actual data
char szBuffer[1024];
int iBytesReceived = zmq_recv(m_pZMQSocket, szBuffer, sizeof(szBuffer), ZMQ_DONTWAIT);
if (iBytesReceived > 0)
{
// process TCP data
}
}
}
注意:此答案需要使用ZMQ 4或更高版本,因为早期版本的ZMQ无法与常规TCP套接字连接进行通信。
答案 1 :(得分:0)
您可以通过覆盖zmq_recv()
在应用主消息循环中使用ZMQ_NOBLOCK
标记来呼叫CWinApp::OnIdle()
。如果套接字上没有数据等待,zmq_recv
将立即返回。如果有数据,请处理它 - 但要注意,如果你做得很慢,你会让应用程序无响应。
编辑:我没有意识到OnIdle
只在消息队列变空时才被调用一次。但是根据MSDN's documentation,您可以返回非零值以永久调用:
OnIdle
并提供0
作为lCount
参数。OnIdle
执行一些处理并返回非零值,表示应再次调用它以进行进一步处理。OnIdle
,递增lCount
参数。OnIdle
完成处理所有空闲任务并返回0
。这告诉消息循环停止调用OnIdle
,直到从消息队列收到下一条消息,此时空闲循环重新开始,参数设置为0
。我还发现this thread on GameDev.net用户说:
我在MFC中使用D3D编写的所有工具都使用OnIdle()函数:
BOOL CD3DEditorApp::OnIdle(LONG lCount)
{
// Call base class first
CWinApp:OnIdle( lCount );
// game stuff...
// Always return true - this asks the framework to constantly
// call the Idle function when it isn't busy doing something
// else.
return TRUE;
}
所以,至少据一个人说,这是一种常见的技术。
答案 2 :(得分:0)
您可以使用自定义Windows消息从线程发送信号。这是一条自定义消息:
#define WM_MY_MESSAGE (WM_APP + 1)
将其发送到具有窗口的线程使用PostMessage或SendMessage到HWND。使用ON_MESSAGE将其添加到窗口的消息映射中。
将它发送到没有窗口的CWinThread派生线程使用PostThreadMessage并使用ON_THREAD_MESSAGE接收它。