(15) 可以获取多个锁而不会陷入死锁的 全局函数 lock ( , ) ,实现的依赖的核心函数是 mutex . try_lock () :
template <class... _LockN> // 该函数非常复杂,略,以后补充。但意思目前已经到了
int _Lock_attempt(const int _Hard_lock, _LockN&... _LkN) { return 3; }
template <class _Lock0, class _Lock1> // 返回值的意思是,若加锁失败,则需要尝试重新加锁,故返回 true。
bool _Lock_attempt_small(_Lock0& _Lk0, _Lock1& _Lk1) // 所以,若加锁成功则本函返回 false
{ // 尝试锁定2个锁,首先锁定_Lk0,然后尝试锁定_Lk1。返回是否再次trv
// attempt to lock 2 locks, by first locking _Lk0, and then trying to lock _Lk1
// returns whether to try again。
_Lk0.lock();
_TRY_BEGIN // try {
if (_Lk1.try_lock()) // if (_Lk1.try_lock()) // 本函加锁成功返 T,锁忙碌返 F
return false; // return false;
_CATCH_ALL // } catch (...) { // 也可能没有错误,捕捉不到错误,不需终止进程
_Lk0.unlock(); // _Lk0.unlock();
_RERAISE; // throw;
_CATCH_END // }
_Lk0.unlock(); // 因为加锁失败,获得的 LK0 锁也释放掉
_STD this_thread::yield(); // // 使本线程让出 CPU
return true; // true 表示本线程再次获得运行时需再次尝试获取这俩锁
}
template <class _Lock0, class _Lock1, class _Lock2, class... _LockN>
void _Lock_nonmember1(_Lock0& _Lk0, _Lock1& _Lk1, _Lock2& _Lk2, _LockN&... _LkN)
{ // 函数重载的复杂情况 lock 3 or more locks, without deadlock
int _Hard_lock = 0;
while (_Hard_lock != -1)
_Hard_lock = _Lock_attempt(_Hard_lock, _Lk0, _Lk1, _Lk2, _LkN...);
}
template <class _Lock0, class _Lock1> // 这是重载的简单情况
void _Lock_nonmember1(_Lock0& _Lk0, _Lock1& _Lk1)
{ // 锁 2 锁,没有死锁,特殊情况下更好的 codegen 和 redu 通用元编程
while ( _Lock_attempt_small(_Lk0, _Lk1) && _Lock_attempt_small(_Lk1, _Lk0)) {}
// 函数 _Lock_attempt_small 加锁成功则返回 false ,加锁失败返回 true
}
template <class _Lock0, class _Lock1, class... _LockN> // lock multiple locks, without deadlock
void lock(_Lock0& _Lk0, _Lock1& _Lk1, _LockN&... _LkN) // 锁定多个锁,无死锁
{ //看源码,本函只有在获取了所有锁之后,才会返回。
_Lock_nonmember1(_Lk0, _Lk1, _LkN...);
} //本函数的实现灵魂是调用 mutex.try_lock(),该函数不会陷入睡眠等待
(16) 全局不阻塞线程拿到多个锁的函数 try_lock (…) 。 本函尝试加锁成功,返回 -1 。锁忙碌则释放所有的锁,返回0 或 1:
template <class... _LockN> // 此函数的调用链一如既往的复杂,暂时略。以后看情能看懂的话况再补充
int _Try_lock_range(const int _First, const int _Last, _LockN&... _LkN) { return -1; }
template <class _Lock0, class _Lock1, class _Lock2, class... _LockN>
int _Try_lock1(_Lock0& _Lk0, _Lock1& _Lk1, _Lock2& _Lk2, _LockN&... _LkN)
{ // try to lock 3 or more locks
return _Try_lock_range(0, sizeof...(_LockN) + 3, _Lk0, _Lk1, _Lk2, _LkN...);
}
template <class _Lock0, class _Lock1> // 函数的重载
int _Try_lock1(_Lock0& _Lk0, _Lock1& _Lk1)
{
if (!_Lk0.try_lock()) // 此函数 try 加锁成功则返回 true,锁忙碌则返回 false
return 0;
_TRY_BEGIN // try {
if (!_Lk1.try_lock()) { // if (!_Lk1.try_lock()) {
_Lk0.unlock(); // _Lk0.unlock();
return 1; // return 1;
} // }
_CATCH_ALL // } catch (...) { // 也可能没有错误,捕捉不到错误,不需终止进程
_Lk0.unlock(); // _Lk0.unlock();
_RERAISE; // throw;
_CATCH_END // }
return -1;
}
template <class _Lock0, class _Lock1, class... _LockN>
int try_lock(_Lock0& _Lk0, _Lock1& _Lk1, _LockN&... _LkN)
{ // try to lock multiple locks
return _Try_lock1(_Lk0, _Lk1, _LkN...);
} // 本函尝试加锁成功,返回 -1 。锁忙碌则释放所有的锁,返回0 或 1
(17) 类型转换运算符 operator bool (),在 unique_lock 的源代码里,出现了类型转换函数,学习一下:
++可见此类型转换运算符函数的书写,没有函数返回值。 举例测试一下:
++ 以及:
++ 以及:
++ 对于 unique_lock 的学习,涉及到线程同步里对时间量的使用,以后再总结与完善所有的源代码。
(18) 函 call_once (…) 的源码 ,这是正确使用该函数的理论依据,加锁互斥的功能,调用了更底层的系统函数:
extern "C" int __std_init_once_begin_initialize(
void** _LpInitOnce, unsigned long _DwFlags, int* _FPending, void** _LpContext) noexcept;
// 这两个系统函数都是成功执行返回 true,失败返回 false。 参数名可以见名知意。
// 系统函数__std_init_once_complete(&_Once._Opaque, 控制标志, 可选的上下文环境)
extern "C" int __std_init_once_complete(
void** _LpInitOnce, unsigned long _DwFlags, void* _LpContext) noexcept;
// #define RTL_RUN_ONCE_INIT_FAILED 0x00000004UL
// #define INIT_ONCE_INIT_FAILED RTL_RUN_ONCE_INIT_FAILED
_INLINE_VAR constexpr unsigned long _Init_once_init_failed = 0x4UL; // 初始化失败的标志
struct once_flag // opaque不透明的 data structure for call_once()
{
void* _Opaque; // 含有一个指针类型的数据成员
once_flag() noexcept : _Opaque(nullptr) {} // 默认构造函数
once_flag(const once_flag&) = delete; // 禁止 copy 构造函数
once_flag& operator=(const once_flag&) = delete; // 禁止 copy 赋值运算符函数
};
struct _Init_once_completer // 类似智能指针或 lock_guard,用于析构上面的 once_flag 对象
{ // 本类型的对象是定义在 call_once(...) 函数内的局部对象
once_flag& _Once; // 数据成员,对 once_flag 类型的数据的引用
unsigned long _DwFlags;
~_Init_once_completer() // 析构函数
{
if ( __std_init_once_complete(&_Once._Opaque, _DwFlags, nullptr) == 0 )
_CSTD abort(); // __declspec(noreturn) void __cdecl abort(void);
}
};
// 在进程全局里生成 once_flag 对象,这里使用其 once_flag对象 的左值引用
template <class _Fn, class... _Args> // call 函数 _Fx(_Ax...) once,只调用形参中的函数一次
void call_once (once_flag& _Once, _Fn&& _Fx, _Args&&... _Ax) // 这一行最重要
noexcept(noexcept(_STD invoke(_STD forward<_Fn>(_Fx), _STD forward<_Args>(_Ax)...)))
{
int _Pending;
// 看意思,函数仅可以被执行一次的设置,是在这里执行的,这里的 nullptr 也是可选的上下文环境
if ( __std_init_once_begin_initialize(&_Once._Opaque, 0, &_Pending, nullptr) == 0 )
_CSTD abort(); // __declspec(noreturn) void __cdecl abort(void);
if (_Pending != 0) // unsigned long _Init_once_init_failed = 0x4UL;
{
_Init_once_completer _Op{_Once, _Init_once_init_failed}; // 本函数内定义的局部变量
_STD invoke(_STD forward<_Fn>(_Fx), _STD forward<_Args>(_Ax)...);
_Op._DwFlags = 0;
}
}
(19) 关于 canll_ince ( … ) 调用的系统函数的描述,谢谢网上的资料与老师:
++ 以及函数 InitOnceComplete (…) 函数原型的解释:
++ 代码演示:
#include <windows.h>
#include <iostream>
INIT_ONCE g_InitOnce = INIT_ONCE_STATIC_INIT;
BOOL CALLBACK InitFunction(PINIT_ONCE pInitOnce, PVOID pParameter, PVOID* lpContext)
{
std::cout << "Initializing..." << std::endl;
// 在这里执行初始化代码
return TRUE; // 返回 TRUE 表示初始化成功
}
int main()
{
BOOL isInitialized = FALSE;
// 尝试进行一次性初始化
isInitialized = InitOnceExecuteOnce(&g_InitOnce, InitFunction, NULL, NULL);
if (isInitialized) {
std::cout << "Initialization completed successfully." << std::endl;
} else {
std::cout << "Initialization failed." << std::endl;
}
return 0;
}
++ 简评:
(20) 全局函数 call_once (…) 的使用举例:
(21)
谢谢