std::mutex 는 (현시점에서)VisualC++ Runtime에서의 구현은 SRWLOCK로 구현하여, Kernel Object의 HANDLE Mutex나 CRITICAL_SECTION 와도 별개가 된다. 진짜 맞는지 조사 해본다.
#include <iostream>
#include <atomic>
#include <mutex>
#include <vector>
#include <memory>
#include <Windows.h>
template<typename Out>
void print_env(Out& out)
{
#define PRINT_MACRO(VAR) out << #VAR ": " << VAR << std::endl
PRINT_MACRO(_MSC_FULL_VER);
PRINT_MACRO(_MSVC_LANG);
PRINT_MACRO(_WIN64);
PRINT_MACRO(_MT);
#ifdef _DEBUG
out << "with _DEBUG" << std::endl;
#else
out << "without _DEBUG" << std::endl;
#endif
PRINT_MACRO(_DLL);
#undef PRINT_MACRO
}
void exec_systeminfo()
{
system("systeminfo | findstr /r \"^OS.버전\"");
system("systeminfo | findstr /r \"^시스템 종류\"");
}
using namespace std;
template<typename T>
size_t lock_loop(atomic_bool& stop, T& lockable)
{
size_t count = 0;
while (!stop)
{
lock_guard<T> l(lockable);
if (++count == 0)
{
throw runtime_error("count overflow");
}
}
return count;
}
struct my_critical_section
{
CRITICAL_SECTION cs;
my_critical_section()
{
::InitializeCriticalSection(&cs);
}
~my_critical_section()
{
::DeleteCriticalSection(&cs);
}
void lock()
{
::EnterCriticalSection(&cs);
}
void unlock()
{
::LeaveCriticalSection(&cs);
}
};
struct my_mutex
{
HANDLE handle;
my_mutex() : handle(::CreateMutex(NULL, FALSE, NULL))
{
}
~my_mutex()
{
if (handle != NULL)
{
::CloseHandle(handle);
}
}
void lock()
{
::WaitForSingleObject(handle, INFINITE);
}
void unlock()
{
::ReleaseMutex(handle);
}
};
struct my_srwlock
{
SRWLOCK srwlock;
my_srwlock()
{
InitializeSRWLock(&srwlock);
}
~my_srwlock()
{
}
void lock()
{
::AcquireSRWLockExclusive(&srwlock);
}
void unlock()
{
::ReleaseSRWLockExclusive(&srwlock);
}
};
template<typename T>
size_t lock_loop_mt(atomic_bool& stop, T& lockable, size_t C = 1)
{
vector<unique_ptr<thread>> threads;
vector<size_t> result(C);
for (size_t i = 0; i < C; ++i)
{
threads.emplace_back(std::make_unique<thread>([&, i]
{
result[i] = lock_loop(stop, lockable);
}));
}
for (auto& pt : threads)
{
pt->join();
}
size_t sum = 0;
for (auto x : result)
{
size_t new_sum = sum + x;
if (sum > new_sum)
{
throw runtime_error("count overflow");
}
sum = new_sum;
}
return sum;
}
template<typename T>
void test(const char* prefix)
{
SYSTEM_INFO info;
::GetSystemInfo(&info);
for (int i = 1; i < static_cast<int>(info.dwNumberOfProcessors); ++i)
{
T m;
atomic_bool stop = false;
size_t result = 0;
thread t([&]
{
result = lock_loop_mt<T>(stop, m, i + 1);
});
this_thread::sleep_for(3s);
stop = true;
t.join();
cout << prefix << i + 1 << "," << result << endl;
}
}
int main()
{
print_env(std::cout);
exec_systeminfo();
cout << "method,threads,count" << endl;
test<mutex>("std::mutex,");
test<my_critical_section>("my_critical_section,");
test<my_mutex>("my_mutex,");
test<my_srwlock>("my_srwlock,");
return 0;
}
lock을 실시하기 위한 동기 오브젝트를 3초간 복수 thread로부터 lock/unlock를 반복하고, 합계 lock/unlock 횟수를 계측한다. 3초 sleep 하고 멈추는 로직이므로 오차는 나름대로라고 생각한다. 동기화 객체는 아래 4개이다.
VS2019 와 2022가 기준이다.
VC++2019