以下代碼使用該QueueUserAPC函式向調度程式執行緒添加命令以同步控制臺輸出。
#include <Windows.h>
#include <iostream>
constexpr auto fenceName = L"GlobalFence";
constexpr auto dispatchCloser = L"GlobalDispatchStop";
constexpr int threadCount = 5;
DWORD WINAPI pure(LPVOID lpThreadParameter)
{
const HANDLE dispatchCloseEvent = OpenEventW(EVENT_ALL_ACCESS, FALSE, dispatchCloser);
while(WaitForSingleObjectEx(dispatchCloseEvent, INFINITE, TRUE) != WAIT_IO_COMPLETION)continue;
return 0;
}
HANDLE dispatcher;
int main()
{
const HANDLE dispatchCloseEvent = CreateEventW(nullptr, TRUE, FALSE, dispatchCloser);
dispatcher = CreateThread(NULL, 1024, &pure, 0, 0, NULL);
const HANDLE g_FenceEvent = CreateEventW(nullptr, TRUE, FALSE, fenceName);
HANDLE threads[threadCount];
for (int i = 0; i < threadCount; i )
{
threads[i] = CreateThread(NULL, 1024*1024,
[](LPVOID) -> DWORD
{
DWORD d = QueueUserAPC(([](ULONG_PTR) {std::cout << "thread opened\n"; }), dispatcher, NULL);
if(d == 0)std::cout << GetLastError() << std::endl;
HANDLE a = OpenEventW(EVENT_ALL_ACCESS, FALSE, fenceName);
WaitForSingleObject(a, INFINITE);
d = QueueUserAPC([](ULONG_PTR) {std::cout << "thread released\n"; }, dispatcher, NULL);
if (d == 0)std::cout << GetLastError() << std::endl;//often reports error 31
return 0;
},
0, 0, NULL);
}
Beep(300, 300);//the length of the delay effects the behavior, somehow.
SetEvent(g_FenceEvent);
SetEvent(dispatchCloseEvent);
WaitForMultipleObjects(threadCount, threads, TRUE, INFINITE);
WaitForSingleObject(dispatcher, INFINITE);
SetEvent(dispatchCloseEvent);
for (int i = 0; i < threadCount; i )
CloseHandle(threads[i]);
CloseHandle(g_FenceEvent);
CloseHandle(dispatchCloseEvent);
}
代碼在大約 40% 的時間內正確執行。有時(雖然這有點罕見)“執行緒打開”文本不會被寫入控制臺正確的次數,但不會報告任何錯誤getLastError()
uj5u.com熱心網友回復:
一旦回圈 pure()接收到它的第一個 APC 通知,回圈就會中斷并pure()退出,終止執行緒。
錯誤 31 是ERROR_GEN_FAILURE,并且根據QueueUserAPC()檔案:
當執行緒處于終止程序中時,呼叫 QueueUserAPC 添加到執行緒的 APC 佇列將失敗,并回傳 (31) ERROR_GEN_FAILURE。
如果您希望調度程式執行緒處理多個 APC 通知,則它需要保持運行。您打算在回圈條件中使用==而不是!=:
while (WaitForSingleObjectEx(dispatchCloseEvent, INFINITE, TRUE) == WAIT_IO_COMPLETION) continue;
這樣,如果等待由于排隊的 APC 而退出,回圈將回傳等待下一個 APC。pure()只有在收到APC 通知以外的回傳值WAIT_OBJECT_0時,例如發出關閉事件信號時,回圈才會中斷,退出以終止執行緒。
我看到的另一個問題是您發出信號dispatchCloseEvent太快了,因此調度程式執行緒可能會停止運行,而受防護的執行緒仍在嘗試將 APC 排入佇列。這就是為什么QueueUserAPC()每個受保護執行緒中的第二次呼叫都會隨機失敗的原因。您需要先等待所有受保護的執行緒完成,然后通知調度程式停止運行。
SetEvent(g_FenceEvent);
WaitForMultipleObjects(threadCount, threads, TRUE, INFINITE);
SetEvent(dispatchCloseEvent); // <-- move here!
WaitForSingleObject(dispatcher, INFINITE);
此外,您的所有執行緒都在泄漏HANDLE它們打開的s OpenEventW()。CloseHandle()根據OpenEventW()檔案,您需要呼叫它們:
使用 CloseHandle 函式關閉句柄。當行程終止時,系統會自動關閉句柄。事件物件在其最后一個句柄關閉時被銷毀。
就此而言,你根本不需要OpenEventW()。您可以通過它們的引數將現有的HANDLEs from傳遞main()給每個執行緒LPVOID:
DWORD WINAPI pure(LPVOID lpThreadParameter)
{
HANDLE dispatchCloseEvent = (HANDLE) lpThreadParameter;
...
return 0;
}
CreateThread(..., &pure, dispatchCloseEvent, ...);
CreateThread(...,
[](LPVOID param) -> DWORD
{
HANDLE a = (HANDLE) param;
...
},
g_fenceEvent, ...);
或者,只需使用全域變數。
無論哪種方式,使用OpenEventW()消除,在呼叫 時CreateEventW()不再需要為事件分配名稱,從而不再將它們暴露于來自外部代碼的潛在干擾。
Also, you are also not closing the dispatcher thread's HANDLE either, like you are closing the fenced threads' HANDLEs.
轉載請註明出處,本文鏈接:https://www.uj5u.com/shujuku/335565.html
上一篇:Python和GetVolumeInformationW和GetDriveTypeW:列出所有磁盤并獲取磁盤資訊和檔案系統標志
下一篇:在具有兩個以上處理器組的雙套接字系統上通過ctypes在Python中使用GetLogicalProcessorInformationEx()
