Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/cplusplus/164.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C++ c++;高负载下的线程辅助程序故障_C++_Multithreading_C++11_Mutex_Condition Variable - Fatal编程技术网

C++ c++;高负载下的线程辅助程序故障

C++ c++;高负载下的线程辅助程序故障,c++,multithreading,c++11,mutex,condition-variable,C++,Multithreading,C++11,Mutex,Condition Variable,我一直在研究一个系统的想法,在这个系统中,我可以有许多工作线程,它们是由一个中央计时器类定期触发的。这里我关心的部分是一个TriggeredWorker,它在循环中使用mutex&conditionVariable方法等待被告知进行工作。它有一个方法trigger,该方法(由不同的线程)调用,以触发要完成的工作。它是一个抽象类,必须为要实现的实际work方法子类化 我有一个测试表明这个机制是有效的。但是,当我通过减少触发间隔来增加负载时,测试开始失败。当我在触发器之间延迟20微秒时,测试是100

我一直在研究一个系统的想法,在这个系统中,我可以有许多工作线程,它们是由一个中央计时器类定期触发的。这里我关心的部分是一个
TriggeredWorker
,它在循环中使用
mutex
&
conditionVariable
方法等待被告知进行工作。它有一个方法
trigger
,该方法(由不同的线程)调用,以触发要完成的工作。它是一个抽象类,必须为要实现的实际
work
方法子类化

我有一个测试表明这个机制是有效的。但是,当我通过减少触发间隔来增加负载时,测试开始失败。当我在触发器之间延迟20微秒时,测试是100%可靠的。当我减少到1微秒时,我开始出现故障,因为执行的工作计数从1000(预期)减少到9869399等值

我的问题是:(1)什么是出了问题,我怎样才能捕捉出出了什么问题,这样我就可以报告它或做些什么?还有,(2)是否有更好的方法,我可以使用,这样会更好?我不得不承认,我的C++经验仅限于过去3个月,尽管我已经和其他语言一起工作了几年。 非常感谢阅读

以下是关键的代码位:

已触发的工作程序头文件:

\ifndef定时器触发\u工作者\u H
#定义定时器触发的工作者
#包括
#包括
类触发器{
私人:
std::mutex mutex;
std::条件变量condVar;
std::原子运行{false};
std::原子就绪{false};
void workLoop();
受保护的:
虚虚功(){};
公众:
void start();
无效停止();
无效触发器();
};
#endif//定时器触发的工作者
触发的工作程序实现:

#包括“TriggeredWorker.h”
void TriggeredWorker::workLoop(){
PLOGD-ready;
bool running=此->正在运行;
返回准备就绪|!正在运行;});
此->准备就绪=错误;
如果(!此->正在运行){
打破
}

PLOGD在
workLoop
获取锁之前调用
worker.trigger()
两次会发生什么?你松开了一个“触发器”。较小的时间间隔意味着测试失败的概率更高,因为在
workLoop
唤醒之前,多次连续调用
worker.trigger()
的概率更高。请注意,没有任何东西可以保证
workLoop
将在
worker.trigger()
之后但在另一个
worker.trigger()
发生之前获取锁,即使这些调用一个接一个地发生(即不是并行的)。这是由操作系统调度程序控制的,我们无法控制它

不管怎样,核心问题是设置两次
ready\uu=true
会丢失信息。与将整数递增两次不同。因此,最简单的解决方案是用
int
替换
bool
,用
==0
检查替换do inc/dec。这种解决方案也称为信号量。更高级(可能更好,尤其是当您需要将一些数据传递给工作者时)的方法是使用(有界?)线程安全队列。这取决于你到底想要实现什么

顺便说一句:除了
stop()
函数(和
start()
函数,但这并不相关)之外,所有的读取和更新都是在锁下进行的。我建议您将
stop()
也锁定(因为它很少被调用),并将原子转换为非原子。目前存在着不必要的原子学开销


顺便说一句:我建议不要使用
thread.detach()
。您应该将
std::thread
对象存储在
TriggeredWorker
上,并添加使用
join
停止
的析构函数。它们不是独立的生命,因此如果没有
detach()
你可以让你的代码更安全(一个永远不会死,没有另一个)。

谢谢你的建议,我将试用它们,稍后再报告。。。问题:您知道您提到的线程安全队列的示例吗?这似乎是个好主意。我想做的是在固定的时间间隔内完成工作,但如果没有时间完成工作,我想放弃这项工作,并报告一些如何。。。也许我需要涉猎操作系统级的东西和/或实时操作系统。。。这对我来说是全新的!好的,我已经做了你建议的大部分事情,特别是非常有用的BTW1和BTW1。我还没有试过信号灯,但我希望它也能起作用。在某个时刻,给定时间,我将尝试线程安全队列的想法。非常感谢您的帮助。@MattDaley线程安全队列有许多不同的实现。只需谷歌“c++线程安全队列”并阅读您获得的所有信息。例如:很高兴你应用了我的两个旁注,但答案的核心肯定更重要。是的,肯定会这样做。这可能是一本非常好的读物,也适合我的用例:
#include "catch.hpp"
#include "TriggeredWorker.h"
#include <thread>

TEST_CASE("Simple worker performs work when triggered") {
    static std::atomic<int> twt_count{0};

    class SimpleTriggeredWorker : public TriggeredWorker {
    protected:
        void work() override {
            PLOGD << "Incrementing counter.";
            twt_count.fetch_add(1);
        }
    };

    SimpleTriggeredWorker worker;

    worker.start();

    for (int i = 0; i < 1000; i++) {
        worker.trigger();
        std::this_thread::sleep_for(std::chrono::microseconds(20));
    }

    std::this_thread::sleep_for(std::chrono::seconds(1));

    CHECK(twt_count == 1000);

    std::this_thread::sleep_for(std::chrono::seconds(1));
    worker.stop();
}