gpt4 book ai didi

c++ - 有没有比下面更好的方法来使用C++并发?

转载 作者:行者123 更新时间:2023-12-03 07:02:05 26 4
gpt4 key购买 nike

我正在尝试学习线程,互斥等的基础知识。遵循here的文档和示例。在下面的代码中,我得到预期的输出。问题:

  • 想确认我是否有任何陷阱?我们如何改善下面的代码?
  • 我的线程在哪一行尝试获取互斥锁或正在等待互斥锁? (是在11号线之后还是11号线?)
  • 可以在线程代码中包括cv.notify_all();吗?
  • #include<mutex>
    #include<iostream>
    #include<thread>
    #include<condition_variable>

    std::mutex mtx;
    std::condition_variable cv;
    int currentlyRequired = 1;

    void work(int id){
    std::unique_lock<std::mutex> lock(mtx); // Line 11
    while(currentlyRequired != id){
    cv.wait(lock);
    }
    std::cout<<"Thread # "<<id<<"\n";
    currentlyRequired++;
    cv.notify_all();
    }

    int main(){
    std::thread threads[10];

    for(int i=0; i<10; i++){
    threads[i] = std::thread(work, i+1);
    }

    for(int i=0; i<10; i++){
    threads[i].join();
    }
    return 0;
    }

    /* Current Output:
    Thread # 1
    Thread # 2
    Thread # 3
    Thread # 4
    Thread # 5
    Thread # 6
    Thread # 7
    Thread # 8
    Thread # 9
    Thread # 10
    Program ended with exit code: 0
    */

    最佳答案

    首先,我建议您在这里阅读文档:cppreference.com
    对于将条件变量用于线程间等待和通知所需要遵循的关键点,您要谨慎得多。
    我认为您的代码可以衡量。
    您的代码将按照您的想法在第11行获得锁。 unique_lock的其他构造函数将采用先前已锁定(由当前线程锁定)或未锁定(由当前线程锁定)的互斥锁,并在请求时将其锁定(此处为lock.lock();)。
    你所拥有的是正确的。您检查持有锁的相关数据。
    等待,然后将其解锁(通过unique_lock)并等待通知。
    收到通知后,它将停止等待,再次将其锁定并循环检查条件。
    最终,条件为真,并且每个线程(仍保持锁定状态)继续进行其“工作”。
    “等待”方面看起来是正确的。 “通知”面也看起来是正确的。
    必须修改包含互斥量的条件数据,以确保检查数据并进入条件变量管理的等待状态的正确同步。
    您正确notify_all()。即使在逻辑上(在此示例中)仅需要唤醒一个线程,也无法选择将其作为notify_one()的目标。
    因此,所有线程都被唤醒(如果挂起),请检查它们的状态,并且恰好其中之一可以识别出该线程并运行。
    常识(和我的经验)是notify_all()不持有锁会更好(并且更有效),因为等待的线程会醒来阻塞(在锁上)。但是我被告知某些平台在锁定状态下可以更好地进行通知。欢迎来到平台依赖的世界...
    因此,就实现条件变量而言,我认为这是有效的,而且几乎是教科书。
    也很高兴看到join()。我有一个关于编码器未加入线程的错误。
    这是您在小规模和高负载情况下无法使用的应用程序之一,但是当应用程序扩展并遇到高负载时,可能会引发问题和困惑。
    我所做的工作没有并行性。
    您已经实现了一条菊花链。这样做的目的是确保只有一个线程同时“执行工作”,并且它们必须严格执行。
    为了利用并发性,我们希望最大化并行度-做“工作”的并行运行线程的数量(即,不是线程间通信的内务处理)和您的代码按字面意思(因为您做对了!)确保确保存在并发性永远不会有一个以上的线程在运行,并且代码由于保持工作而保证比单线程应用程序要慢一些(这将是for循环!)。
    因此,它在程序正确性方面获得了最高的评价,但在有用方面没有任何评价!
    在我看来,cplusplus.con和cppreference.com上的两个示例都稍好一点。
    最好的介绍性例子是某种生产者消费者模型。
    这更接近于实际有用的模式。
    尝试简单的操作,例如一个生产者线程通过整数进行累加,然后多个消费者线程将它们平方并输出。
    关键点在于,如果您做对了,则方块通常不会按顺序出现。
    这些示例就像通过阶乘教学递归一样。当然,它是递归的,但是递归是计算阶乘的一种糟糕方法!
    确保您的多线程(其他示例)有效,但是它们完全是并行的,无济于事!
    请不要以为批评。作为“ dentry 切割”运动,您所拥有的是一流的。下一个任务是做一些有用的并发操作!
    问题是我们不需要条件变量!根据定义,它们使线程彼此等待,从而减少了并行性。我们需要他们,他们会做自己的工作。但是,减少相互等待量(在锁定或条件下)的设计通常更好,因为这里的敌人正在等待,阻塞或(最差)旋转,而不是暂停的等待/阻塞。
    这是为您的任务设计的更好的方案。更好,因为它完全避免了条件变量!!

    #include<mutex>
    #include<iostream>
    #include<thread>

    std::mutex mtx;
    int currentlyRequired = 1;

    void work(int id){
    std::lock_guard<decltype(mtx)> guard(mtx);
    const auto old{currentlyRequired++};
    std::cout<<"Thread # "<<id<<" "<< old << "-> " <<currentlyRequired<< "\n";
    }

    int main(){
    std::thread threads[10];

    for(int i=0; i<10; i++){
    threads[i] = std::thread(work, i+1);
    }

    for(int i=0; i<10; i++){
    threads[i].join();
    }
    std::cout << "Final result: " << currentlyRequired << std::endl;
    return 0;
    }
    标本输出:
    Thread # 7 1-> 2
    Thread # 8 2-> 3
    Thread # 9 3-> 4
    Thread # 10 4-> 5
    Thread # 6 5-> 6
    Thread # 5 6-> 7
    Thread # 4 7-> 8
    Thread # 3 8-> 9
    Thread # 2 9-> 10
    Thread # 1 10-> 11
    Final result: 11
    哪个线程执行哪个增量将有所不同。但是最终结果总是11。
    仍然没有好处,因为仍然没有并行性,因为工作都在一个锁下完成。这就是为什么我们需要更有趣的任务。
    玩得开心。

    关于c++ - 有没有比下面更好的方法来使用C++并发?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/64464193/

    26 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com