後來,我爲Windows API函數編寫了一個簡單的包裝類。我編寫了一組單元測試來驗證該類生成的結果是否與直接調用API相匹配。在線程中使用GetLastError
最近我回過頭來添加有關在不同線程中使用包裝類的單元測試。我發現::GetLastError
函數存在一些問題。據MSDN功能應該保留的最後一個錯誤代碼每個線程:
檢索調用線程的最後一個錯誤代碼值。最後的錯誤代碼以每個線程爲基礎進行維護。多線程不會覆蓋彼此的最後一個錯誤代碼。
我發現在某些情況下,最後的錯誤代碼實際上變成了零。我已成功地複製問題的單元測試之外與下面簡單的程序:
#include "stdafx.h"
#include <condition_variable>
#include <mutex>
#include <thread>
#include <Windows.h>
int main(int argc, char* argv[])
{
::DWORD setError1 = 123;
::DWORD setError2 = 456;
// scenario 1 - show that main thread not polluted by sub-thread
const auto act1 = [](::DWORD errorNo)
{
::SetLastError(errorNo);
const auto a = ::GetLastError(); // a = 123
};
::SetLastError(setError2);
const auto b = ::GetLastError(); // b = 456
const auto c = ::GetLastError(); // c = 456
std::thread sub1(act1, setError1);
sub1.join();
const auto d = ::GetLastError(); // d = 0 - WHY???
// scenario 2 - show that sub thread not polluted by main thread
std::condition_variable conditional;
std::mutex mutex;
bool flag = false;
::DWORD e;
const auto act2 = [&mutex, &flag, &e, &conditional](::DWORD errorNo)
{
std::unique_lock<std::mutex> lock(mutex);
::SetLastError(errorNo);
conditional.wait(lock, [&flag] { return flag; });
e = ::GetLastError(); // e = 456 in Windows 8.1, 0 in Windows 10.0.10240.0 - WHY???
};
std::thread sub2(act2, setError2);
{
std::lock_guard<std::mutex> guard(mutex);
::SetLastError(setError1);
flag = true;
}
conditional.notify_all();
sub2.join();
const auto f = ::GetLastError(); // f = 123;
return 0;
}
我遇到的問題是與d
和e
:
d - 我發現,最後主線程的錯誤代碼在使用子線程時看到復位爲零。
ë - 當使用Windows 10 SDK,我發現子線程看到最後一個錯誤的復位至零,而守候在
std::condition_variable
。這在使用Windows 8.1 SDK時不會重置。
有人可以幫助解釋我看到的結果嗎?這是Windows API中的一個錯誤,是微軟C++實現中的一個錯誤?或者在我自己的代碼中的錯誤?
因此微軟的標準庫的實現,或者至少它的線程實現,使用Windows API? – camelCase
當然。不通過Windows API就無法創建線程。同樣也適用於同步對象。這些將在可能的情況下在系統對象之上實現。 –
^^大衛說。I/O,線程,線程間同步等必須由操作系統管理,否則會出現混亂。如果您通過庫明確或間接地進行API調用,並且失敗,則應該在下一步調用GetLastError,否則可能會將錯誤信息丟失給另一個成功的API。 –