当前位置: 首页 > 知识库问答 >
问题:

将std::condition\u变量与原子一起使用

乜思淼
2023-03-14

关于如何处理原子,以及如何处理std::condition\u变量,有几个问题。但我的问题是,我下面的用法是否正确?

三个线程,一个ctrl线程,在取消暂停其他两个线程之前执行准备工作。ctrl线程还能够在工作线程(发送方/接收方)处于紧密的发送/接收循环时暂停它们。使用原子的想法是在没有设置暂停布尔值的情况下,使紧循环更快。

class SomeClass
{

public:
    //...                                                                                                                                                                                                                                                                                                                                                                                   
    // Disregard that data is public...                                                                                                                                                                                                                                                                                                                                                     

    std::condition_variable cv; // UDP threads will wait on this cv until allowed                                                                                                                                                                                                                                                                                                           
                                // to run by ctrl thread.                                                                                                                                                                                                                                                                                                                                   
    std::mutex cv_m;
    std::atomic<bool> pause_test_threads;
};

void do_pause_test_threads(SomeClass *someclass)
{
    if (!someclass->pause_test_threads)
    {
        // Even though we use an atomic, mutex must be held during                                                                                                                                                                                                                                                                                                                          
        // modification. See documentation of condition variable                                                                                                                                                                                                                                                                                                                            
        // notify_all/wait. Mutex does not need to be held for the actual                                                                                                                                                                                                                                                                                                                   
        // notify call.                                                                                                                                                                                                                                                                                                                                                                     
        std::lock_guard<std::mutex> lk(someclass->cv_m);
        someclass->pause_test_threads = true;
    }
}

void unpause_test_threads(SomeClass *someclass)
{
    if (someclass->pause_test_threads)
    {
        {
            // Even though we use an atomic, mutex must be held during                                                                                                                                                                                                                                                                                                                      
            // modification. See documentation of condition variable                                                                                                                                                                                                                                                                                                                        
            // notify_all/wait. Mutex does not need to be held for the actual                                                                                                                                                                                                                                                                                                               
            // notify call.                                                                                                                                                                                                                                                                                                                                                                 
            std::lock_guard<std::mutex> lk(someclass->cv_m);
            someclass->pause_test_threads = false;
        }
        someclass->cv.notify_all(); // Allow send/receive threads to run.                                                                                                                                                                                                                                                                                                                   
    }
}

void wait_to_start(SomeClass *someclass)
{
    std::unique_lock<std::mutex> lk(someclass->cv_m); // RAII, no need for unlock.                                                                                                                                                                                                                                                                                                          
    auto not_paused = [someclass](){return someclass->pause_test_threads == false;};
    someclass->cv.wait(lk, not_paused);
}

void ctrl_thread(SomeClass *someclass)
{
    // Do startup work                                                                                                                                                                                                                                                                                                                                                                      
    // ...                                                                                                                                                                                                                                                                                                                                                                                  
    unpause_test_threads(someclass);

    for (;;)
    {
        // ... check for end-program etc, if so, break;                                                                                                                                                                                                                                                                                                                                     
        if (lost ctrl connection to other endpoint)
        {
            pause_test_threads();
        }
        else
        {
            unpause_test_threads();
        }
        sleep(SLEEP_INTERVAL);

    }

    unpause_test_threads(someclass);
}

void sender_thread(SomeClass *someclass)
{
    wait_to_start(someclass);
    ...
    for (;;)
    {
        // ... check for end-program etc, if so, break;                                                                                                                                                                                                                                                                                                                                     
        if (someclass->pause_test_threads) wait_to_start(someclass);
        ...
    }
}

void receiver_thread(SomeClass *someclass)
{
    wait_to_start(someclass);
    ...
    for (;;)
    {
        // ... check for end-program etc, if so, break;                                                                                                                                                                                                                                                                                                                                     
        if (someclass->pause_test_threads) wait_to_start(someclass);
        ...
    }

共有3个答案

陆洲
2023-03-14

原子数据不需要另一个同步,它是无锁算法和数据结构的基础。

void do_pause_test_threads(SomeClass *someclass)
{
    if (!someclass->pause_test_threads)
    {
        /// your pause_test_threads might be changed here by other thread
        /// so you have to acquire mutex before checking and changing
        /// or use atomic methods - compare_exchange_weak/strong,
        /// but not all together                                                                                                                                                                                                                                                                                                                                                             
        std::lock_guard<std::mutex> lk(someclass->cv_m);
        someclass->pause_test_threads = true;
    }
}
郎睿
2023-03-14

通常,您希望处理这样一个事实,即变量是原子的,与它如何处理条件变量无关。

如果与条件变量交互的所有代码都遵循通常的模式,即在查询/修改之前锁定互斥体,并且与条件变量交互的代码不依赖于不与条件变量交互的代码,那么即使它包装了原子互斥体,它也将继续是正确的。

通过快速阅读伪代码,这似乎是正确的。然而,伪代码通常不能很好地替代多线程代码中的真实代码。

当原子读取表示您可能想要或不想要时,只等待条件变量(并锁定互斥)的“优化”可能是优化。您需要分析吞吐量。

王亮
2023-03-14

我查看了您的操作条件变量和原子的代码,它似乎是正确的,不会引起问题。

若在谓词中检查共享变量和等待条件之间发生写入共享变量,则可能会出现问题。考虑以下内容:

>

控制线程将共享变量设置为true

控制线程发送通知,任何人都不会收到,因为没有线程等待条件变量。

等待线程等待条件变量。由于通知已经发送,它将等到下一次虚假唤醒,或者下次控制线程发送通知时再发送。潜在的不确定等待。

从共享原子变量读取而不锁定通常是安全的,除非它引入了TOCTOU问题。

在您的情况下,您正在读取共享变量以避免不必要的锁定,然后在锁定后再次检查它(在条件调用中)。这是一个有效的优化,称为双重检查锁定,我在这里没有看到任何潜在的问题。

您可能需要检查原子

 类似资料:
  • > 两个相互冲突的评估都是原子操作(请参见STD::atomic) 其中一个冲突的评估发生在另一个之前(请参见STD::MEMORY_ORDER) 如果发生数据争用,程序的行为是未定义的。

  • 问题内容: 我对正则表达式很糟糕,但是我想知道是否可以将ng-pattern与变量一起使用 例如, 其中validationCode是控制器中附加到$ scope的变量 如果 则ng-pattern将是 但这不起作用,似乎我需要创建一个我真的不想要的自定义指令 问题答案: 需要一个正则表达式。 从Angular的文档中有关: 如果该值与模式表达式不匹配,则设置模式验证错误键。期望值用于内联模式或定

  • 条件变量(Condition variables)提供了同步语义,它会将一个线程阻塞住(block)直到被其他线程所唤醒(notify)或到达了系统超时时间。 不过Stroustrup尚未完成此主题,期待中。 参看: Standard: 30.5 Condition variables(thread.condition) (翻译:interma)

  • 问题内容: 指定带有透明度的渐变颜色时,是否可以使用CSS变量,例如 问题答案: 您可以使用变量,但不能从CSS中的单个十六进制值中采样各个红色,绿色和蓝色分量。 如果您只是想将alpha分量应用于现有的RGB三元组,则可以将整个三元组指定为用逗号分隔的十进制值列表,而不是十六进制值,并将其作为单个不透明标记直接替换为函数: 如果要使用来指定和控制各个R,G和B值,则需要为每个颜色分量指定一个变量

  • 我给你的是不可变的。试试看。我需要的是能够指定一个类,所以我定义了 由immutables生成的生成器不接受此选项 因为它需要一门课 用@Value注释类型。参数很好地创建了一个有效的方法 但结果是一个实例,而不是一个生成器,因此只能使用withers设置后续值。

  • 我想在gradle中使用JMH,一个OpenJDK微基准工具。然而,我得到了关于编译的NPE。另一方面,JMH在使用maven时可以工作。 我没有发布任何,因为它是基本的-应用java插件并添加对JHM工具的依赖()。 我试过这里写的东西,但没有成功。 我还需要做什么?我想设置一个代理,但我还是没有弄清楚。 例外情况: