У меня есть класс потока обработки событий, который позволяет мне вызывать события из других потоков, не прерывая их работу. Когда вызывается деструктор, я отправляю сообщение о выходе в поток, но его цикл сообщений, похоже, не получает это сообщение.
#include <iostream>
using namespace std;
#include <windows.h>
#define WIN32_LEAN_AND_MEAN
#define ET_EVENTPUSH 7854
#define ET_QUITLOOP 6581
#define EVENT_HANDLER void
typedef void (*EVENT)(LPVOID);
//# event handler thread
class event_thread
{
private:
//vars
HANDLE m_thread; //event loop thread
DWORD m_id; //thread id
//thread procedure
static DWORD WINAPI listener_loop(LPVOID);
public:
//constructor
event_thread();
~event_thread();
//functions
void push_event(EVENT, LPVOID);
};
//# event handler thread
event_thread::event_thread()
{
//create thread
m_thread = CreateThread(NULL, 0, listener_loop, NULL, 0, &m_id);
}
event_thread::~event_thread()
{
DWORD dw;
//stop thread
if (m_thread)
{
dw = GetLastError();
PostThreadMessage(m_id, ET_QUITLOOP, 0, 0);
dw = GetLastError();
WaitForSingleObject(m_thread, INFINITE);
CloseHandle(m_thread);
}
}
void event_thread::push_event(EVENT e, LPVOID p)
{
PostThreadMessage(m_id, ET_EVENTPUSH, (UINT)e, (UINT)p);
}
DWORD WINAPI event_thread::listener_loop(LPVOID param)
{
MSG msg;
#define event_type msg.message
#define event_call ((EVENT)(msg.wParam))
#define event_param (LPVOID)(msg.lParam)
//while thread is alive
while (GetMessage(&msg, NULL, 0, 0))
{
switch (event_type)
{
case ET_EVENTPUSH:
event_call(event_param);
break;
case ET_QUITLOOP:
return 0;
}
}
return 0;
}
EVENT_HANDLER asda(LPVOID as)
{
cout << (int)as << endl;
}
int main()
{
event_thread thr;
int i = 1;
while (!GetAsyncKeyState(VK_NUMPAD1))
{
thr.push_event(asda, (LPVOID)(i++));
Sleep(20);
}
return 0;
}
GetMessage
не возвращается, когда я отправляю сообщение о выходе. И используя GetLastError()
после PostThreadMessage
звонок дает 1444 ERROR_INVALID_THREAD_ID
,
Я не хочу использовать PeekMessage
потому что он использует 100% CPU, ни Sleep
потому что это видимо плохая практика.
Спасибо
ERROR_INVALID_THREAD_ID
означает, что вы пытались опубликовать сообщение с идентификатором потока, который не имеет очереди сообщений. Таким образом, вы пытались отправить сообщение до запуска цикла сообщений потока или после того, как поток уже был прерван.
Когда ваш поток начинает работать, он должен сначала отправить сообщение самому себе, чтобы создать очередь сообщений, затем установить сигнал, указывающий, что он готов к приему сообщений, и, наконец, войти в цикл обработки сообщений. В конструкторе вашего класса после вызова CreateThread()
, он может дождаться этого сигнала, а затем выйти.
Попробуй это:
#include <iostream>
using namespace std;
#include <windows.h>
#define WIN32_LEAN_AND_MEAN
#define ET_START WM_USER+1
#define ET_EVENTPUSH WM_USER+2
#define ET_QUITLOOP WM_USER+3
#define EVENT_HANDLER void
typedef void (*EVENT)(LPVOID);
//# event handler thread
class event_thread
{
private:
//vars
HANDLE m_thread; //event loop thread
DWORD m_id; //thread id
bool m_ready; // message queue is ready
//thread procedure
static DWORD WINAPI listener_loop(LPVOID);
public:
//constructor
event_thread();
~event_thread();
//functions
bool push_event(EVENT, LPVOID);
};
//# event handler thread
event_thread::event_thread()
{
//create thread
m_ready = false;
m_thread = CreateThread(NULL, 0, &listener_loop, this, 0, &m_id);
if (m_thread)
{
while (!m_ready)
Sleep(10);
}
}
event_thread::~event_thread()
{
//stop thread
if (m_thread)
{
if (!PostThreadMessage(m_id, ET_QUITLOOP, 0, 0))
{
DWORD dw = GetLastError();
...
}
WaitForSingleObject(m_thread, INFINITE);
CloseHandle(m_thread);
}
}
bool event_thread::push_event(EVENT e, LPVOID p)
{
return PostThreadMessage(m_id, ET_EVENTPUSH, (WPARAM)e, (LPARAM)p);
}
DWORD WINAPI event_thread::listener_loop(LPVOID param)
{
event_thread *pThis = (event_thread*)param;
MSG msg;
#define event_type msg.message
#define event_call ((EVENT)(msg.wParam))
#define event_param (LPVOID)(msg.lParam)
PostThreadMessage(pThis->m_id, ET_START, 0, 0);
pThis->m_ready = true;
//while thread is alive
while (GetMessage(&msg, NULL, 0, 0) > 0)
{
switch (event_type)
{
case ET_EVENTPUSH:
event_call(event_param);
break;
case ET_QUITLOOP:
PostQuitMessage(0);
break;
}
}
return 0;
}
Если вам не нравится Sleep()
цикл вы можете заменить m_ready
с ожидаемым событием, а затем использовать SetEvent()
а также WaitForSingleObject()
,
Других решений пока нет …