【问题标题】:Infinite loop scenario in custom throttler (race condition?)自定义节流器中的无限循环场景(竞争条件?)
【发布时间】:2020-05-17 11:15:02
【问题描述】:

我编写了一个自定义节流器来处理当某些客户端(即进行外部调用)出现退避异常并需要在进行另一个 API 调用之前休眠时的场景。

我的内存转储有一个奇怪的场景:卡在无限循环中,尽管我看不出有什么原因。 此应用程序有许多线程,并且所有客户端调用都使用以下代码进行限制。 对某些 API 的调用是这样完成的:

bool res = DoSomeAction([&]{ /* call client m_client.callAPI()*/ return true}

内存转储显示只有 1 个线程在工作(通常有 10 个),并且 m_isThrottling 设置为 true,因此整个应用程序一直在运行。

这种情况怎么会发生?有什么更好的实现建议(带 m_ 前缀的变量表示类变量,m_throttlingTime 和 m_isThrottling 是静态类变量)?

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;
}

【问题讨论】:

    标签: c++ concurrency throttling


    【解决方案1】:

    无法保证您的线程中m_isTrhottling 的值将永远获得其真实值。事实上,每个线程都有自己的世界观,所以在写给m_isThrottling的另一个线程的视图中,值是true,但你的第三个看到的是不同的画面。

    锁是线程世界观同步的一种方式。如果您使用锁而不是循环,则该值将在您的等待线程中更新。

    修复:

    • 要么使用锁
    • std::atomic&lt;bool&gt; 也可以保证同步。

    澄清编辑:类变量上下文中的关键字static,与内存模型无关。在这种情况下,关键字只是说明变量属于类而不属于任何特定对象。这与线程的世界观(或缺少)的同步完全无关。 static 与线程相关的唯一地方是函数内部的静态变量初始化,但情况并非如此(函数静态变量和初始化都不是)

    【讨论】:

    • m_isThrottling 和 m_throttlingTime 是静态变量,我在写入时使用锁,我试图最小化性能锁
    • 但是您没有锁定读取。由于没有锁,读取线程的内存与所有其他线程不同步。您可以使用std::atomic 仅同步一个变量。至于它是静态的,在这种情况下它没有效果。
    • 但是如果 m_isThrottling 是静态的,它应该对所有线程都相同。我希望它在某个时候设置为 false(在节流完成后)。读锁有什么帮助?它坚持为真,永远只剩下 1 个线程。
    • @damule 我认为您将 C++ 与另一种语言混淆了。在 C++ 中,静态对内存模型没有影响。静态只使变量属于类而不属于对象,这样该类的所有对象之间只共享一个副本,并且可以在没有任何对象的情况下访问它。
    • @damule 将关键字static 与线程安全联系起来的唯一方法是函数内部静态变量的初始化:stackoverflow.com/questions/1661529/…
    猜你喜欢
    • 1970-01-01
    • 2013-08-26
    • 1970-01-01
    • 2011-03-24
    • 1970-01-01
    • 2018-07-24
    • 1970-01-01
    • 2015-04-29
    • 1970-01-01
    相关资源
    最近更新 更多