自定义节流器中的无限循环场景(竞争条件?)
Infinite loop scenario in custom throttler (race condition?)
我编写了一个自定义节流器来处理某些客户端(正在进行外部调用)出现退避异常并且需要在进行另一个 API 调用之前休眠的情况。
我有一个内存转储,其中有一个奇怪的场景:卡在无限循环中,尽管我看不出有什么原因。
此应用程序有许多线程,所有客户端调用都使用以下代码进行了限制。
调用一些 API 是这样完成的:
bool res = DoSomeAction([&]{ /* call client m_client.callAPI()*/ return true}
内存转储显示只有 1 个线程在工作(通常有 10 个),并且 m_isThrottling 设置为 true,因此整个应用程序永远 运行。
怎么会出现这种情况?任何更好实施的建议(带有 m_ 前缀的变量表示 class 变量,m_throttlingTime 和 m_isThrottling 是静态 class 变量)?
template<typename T>
bool ThrottlerClass::DoSomeAction(T && lambda)
{
for (int retriesCount = 3; retriesCount > 0; --retriesCount)
{
while (m_isThrottling) //dump is stuck here
{
Sleep(10000);
}
try
{
return lambda();
}
catch (const std::exception& ex)
{
int time = m_client->GetThrottlingTimeMS(); //the client got exception making API call and saves it
if (time > 0)
{
ExclusiveLock lock(&m_throttlingMutex); //custom scope mutex
m_isThrottling = true;
if (time > m_throttlingTime)
m_throttlingTime = time;
}
if (m_throttlingTime > 0)
{
Sleep(m_throttlingTime);
{
ExclusiveLock lock(&m_throttlingMutex);
m_isThrottling = false;
m_throttlingTime = 0;
}
}
continue;
}
}
return false;
}
无法保证您线程中 m_isTrhottling
的值将永远获得其真实值。事实上,每个线程都有自己的世界观,所以在另一个线程的视图中,写入 m_isThrottling
,值是 true
,但是你的第三个线程看到了不同的画面。
锁是线程世界观同步的一种方式。如果您使用锁而不是循环,该值将在您的等待线程中更新。
修复:
- 要么使用锁
- 或
std::atomic<bool>
也保证了同步。
澄清编辑:class变量上下文中的关键字static
与内存模型无关。在这种情况下,关键字只是声明该变量属于 class 而不是任何特定对象。这与线程世界观(或缺乏)的同步完全无关。 static
与线程相关的唯一地方是函数内部的静态变量 初始化 ,但事实并非如此(既不是函数静态变量,也不是初始化)
我编写了一个自定义节流器来处理某些客户端(正在进行外部调用)出现退避异常并且需要在进行另一个 API 调用之前休眠的情况。
我有一个内存转储,其中有一个奇怪的场景:卡在无限循环中,尽管我看不出有什么原因。 此应用程序有许多线程,所有客户端调用都使用以下代码进行了限制。 调用一些 API 是这样完成的:
bool res = DoSomeAction([&]{ /* call client m_client.callAPI()*/ return true}
内存转储显示只有 1 个线程在工作(通常有 10 个),并且 m_isThrottling 设置为 true,因此整个应用程序永远 运行。
怎么会出现这种情况?任何更好实施的建议(带有 m_ 前缀的变量表示 class 变量,m_throttlingTime 和 m_isThrottling 是静态 class 变量)?
template<typename T>
bool ThrottlerClass::DoSomeAction(T && lambda)
{
for (int retriesCount = 3; retriesCount > 0; --retriesCount)
{
while (m_isThrottling) //dump is stuck here
{
Sleep(10000);
}
try
{
return lambda();
}
catch (const std::exception& ex)
{
int time = m_client->GetThrottlingTimeMS(); //the client got exception making API call and saves it
if (time > 0)
{
ExclusiveLock lock(&m_throttlingMutex); //custom scope mutex
m_isThrottling = true;
if (time > m_throttlingTime)
m_throttlingTime = time;
}
if (m_throttlingTime > 0)
{
Sleep(m_throttlingTime);
{
ExclusiveLock lock(&m_throttlingMutex);
m_isThrottling = false;
m_throttlingTime = 0;
}
}
continue;
}
}
return false;
}
无法保证您线程中 m_isTrhottling
的值将永远获得其真实值。事实上,每个线程都有自己的世界观,所以在另一个线程的视图中,写入 m_isThrottling
,值是 true
,但是你的第三个线程看到了不同的画面。
锁是线程世界观同步的一种方式。如果您使用锁而不是循环,该值将在您的等待线程中更新。
修复:
- 要么使用锁
- 或
std::atomic<bool>
也保证了同步。
澄清编辑:class变量上下文中的关键字static
与内存模型无关。在这种情况下,关键字只是声明该变量属于 class 而不是任何特定对象。这与线程世界观(或缺乏)的同步完全无关。 static
与线程相关的唯一地方是函数内部的静态变量 初始化 ,但事实并非如此(既不是函数静态变量,也不是初始化)