目前,貌似 lldb 还不支持 thread_local 变量的显示,有时需要debug,不容易搞定。
那我们就帮这种变量搞个镜子,用同类型非 thread_local 变量,在任何赋值的时候把值赋给它,通过一个镜子看到它。
以下是 thread_local 的典型用例:分级锁,通过将锁分级,解决在不得不多次互斥的程序中避免死锁问题。
先加高级锁,再加低级锁,只要顺序统一,基本很难死锁,并且用错级别会直接报错,不会出现运行时的 幽灵bug。
为了实现级别比较,我们为每个线程设计一个线程存储期静态变量:
static thread_local uint32_t thisThreadLevelVal;
为了监视此线程存储期静态变量,我们添加一个镜子:
uint32_t thisValPtr = ULONG_MAX;
并初始化为与线程存储期静态变量相同值,并使得两变量始终一致。间接进行捕获。
#include <iostream>
#include <mutex>
#include <thread>
struct levelMutex
{
explicit levelMutex(uint32_t value)
: levelVal(value)
, preLevelVal(0)
{}
void lock()
{
checkForLevelErr();
inMutex.lock();
updateLevelVal();
}
void unlock()
{
if (thisThreadLevelVal != levelVal)
{
throw std::logic_error("mutex level err");
}
thisThreadLevelVal = preLevelVal;
thisValPtr = thisThreadLevelVal;
inMutex.unlock();
}
auto tryLock() -> bool
{
checkForLevelErr();
if (!inMutex.try_lock())
{
return false;
}
updateLevelVal();
return true;
}
private:
void checkForLevelErr() const
{
if (thisThreadLevelVal <= levelVal)
{
throw std::logic_error("mutex level err");
}
}
void updateLevelVal()
{
preLevelVal = thisThreadLevelVal;
thisThreadLevelVal = levelVal;
thisValPtr = thisThreadLevelVal;
fprintf(stdout, "%d\n", thisThreadLevelVal);
}
//内部互斥量
std::mutex inMutex;
//等级值
uint32_t const levelVal;
//前一个等级值
uint32_t preLevelVal;
//本线程等级值
static thread_local uint32_t thisThreadLevelVal;
uint32_t thisValPtr = ULONG_MAX;
};
thread_local uint32_t levelMutex::thisThreadLevelVal(ULONG_MAX);
levelMutex highLevelMutex(10000);
levelMutex lowLevelMutex(5000);
levelMutex otherMutex(6000);
auto doLowLevelStuff() -> int
{
return 1;
}
auto lowLevelFunc() -> int
{
std::lock_guard<levelMutex> lk(lowLevelMutex);
return doLowLevelStuff();
}
void highLevelStuff(int someParam)
{
std::cout << someParam << std::endl;
}
void highLevelFunc()
{
std::lock_guard<levelMutex> lk(highLevelMutex);
highLevelStuff(lowLevelFunc());
}
void thread_a()
{
highLevelFunc();
}
void doOtherStuff()
{
std::cout << "other" << std::endl;
}
void otherStuff()
{
highLevelFunc();
doOtherStuff();
}
void thread_b()
{
std::lock_guard<levelMutex> lk(otherMutex);
otherStuff();
}
auto main() -> int
{
std::thread ta(thread_a);
std::thread tb(thread_b);
ta.join();
tb.join();
return 0;
}
基本解决。
最近学并发编程,发现 lldb 和 gdb 对多线程调试并不完美,本文内容的写作原因是,涉及线程存储期变量调试的内容很少,整个互联网,只有 StackOverflow 有两个相关回答,但是答案是 lldb 不支持,所以,只能见招拆招了。