如何将这种特殊情况的线程与 C++ std::thread 同步

How to synchronize this special case of threads with C++ std::thread

提问人:TommyBe 提问时间:11/14/2023 最后编辑:genpfaultTommyBe 更新时间:11/16/2023 访问量:69

问:

对于以下情况,我需要一些帮助:我有一个主程序,可以启动与线程相同的函数的多个实例。被调用的函数至少分为两部分。首先,每个实例都必须执行函数的第 1 部分,然后才能继续执行第 2 部分。在这里,我需要同步它们。为了更易于理解,我将编写一些示例代码:

void TMainForm::foo(int numThreads)
{
    std::cout << "Part 1: do some work" << std::endl;

    /* HERE NEEDS TO BE SOMETHING THAT CHECKS WHETHER EVERY INSTANCE HAS DONE PART 1 !!*/

    std::cout << "Part 2: do some work" << std::endl;
}
//---------------------------------------------------------------------------

void TMainForm::StartingThreads()
{
    int threads = std::thread::hardware_concurrency();
    std::vector<std::thread> t(threads);

    for (int i = 0; i < threads; i++)
        t[i] = std::thread(foo, threads);

    for (int i = 0; i < threads; i++)
        t[i].join();
}
//--------------------------------------------------------------------------- 

我尝试使用互斥锁,但它对我不起作用。我递增了一个原子变量(比如说原子 a),如果该值小于 numThreads,我执行了互斥锁::锁定。当最后一个实例完成它的工作时,它会完成一个互斥锁::解锁。但是我的线程在第 1 部分之后总是卡住了。

有人有什么想法吗?

C++ 互斥 锁线程同步 stdmutex

评论

2赞 psmears 11/14/2023
我怀疑你想使用屏障
0赞 463035818_is_not_an_ai 11/14/2023
重构以使这两个部分处于不同的功能中。然后首先让一些线程在第 1 部分工作,将它们全部连接起来,它们使一些线程在第 2 部分工作
1赞 HolyBlackCat 11/14/2023
使用 std::latch(或 ,这是一个多用途闩锁)。std::barrier
0赞 TommyBe 11/14/2023
感谢您的快速回复!我没有听说过std::latch。这对我来说似乎是正确的!非常感谢!
0赞 Red.Wave 11/14/2023
反对票从何而来?这似乎是一个非常有效和有用的问题。
0赞 Red.Wave 11/14/2023
如果您可以升级到 C++20 - 如果您访问 / 就是这种情况 - 那么请考虑使用 over ;前者自动销毁。std::latchstd::barrierstd::jthreadstd::threadjoin

答:

0赞 Joseph Larson 11/15/2023 #1

如果你使用的是 C++ 20,那么其他人已经给了你答案。如果你年满 17 岁,那么我会做一个你可以疯狂地进行单元测试的课程。

class Barrier {
public:
    Barrier(int dc = 1): desiredCount(dc) {}

    void done() {
        std::unique_lock<std::mutex> lock(mutex);
        ++doneCount;
        condVar.notify_all();
    }

    void waitReady() {
        std::unique_lock<std::mutex> lock(mutex);
        while (doneCount < desiredCount) {
            condVar.wait(lock);           
        }
    }

private:
    std::mutex mutex;
    std::cond_var condVar;
    int desiredCount = 1;
    int doneCount = 0;
};

然后在代码中,将其中一个初始化为线程数,然后在线程中:

barrier.done();
barrier.waitReady();

我正在写这个 - 可能不是完美的代码。还有其他方法可以使用 wait(您可以传递谓词),但这是我经常使用的模式。

评论

0赞 TommyBe 11/15/2023
谢谢!这对我来说也很好,尤其是对于较旧的 c++ 版本!我想在您的 done() 函数中,您想增加 doneCount 而不是 desiredCount?我不能给你点赞,因为我是新来的。
0赞 Joseph Larson 11/16/2023
@TommyBe 你对 done() 方法的看法是正确的,我会解决这个问题。