multithreading - 同步3个线程以打印顺序输出

标签 multithreading c++11

采访中有人问我这个问题。我真是一无所知。
因此,我决定学习一些多线程技术,并希望找到该问题的答案。

我需要使用3个线程来打印输出:01020304050607 ...

  • 线程1:打印0
  • Thread2:打印奇数
  • Thread3:打印偶数
  • #include <iostream>
    #include <thread>
    #include <mutex>
    #include <condition_variable>
    
    std::mutex m;
    std::condition_variable cv1, cv2, cv3;
    
    int count = 0;
    
    void printzero(int end)
    {
        while (count <= end)
        {
            std::unique_lock<std::mutex> lock(m);
            cv1.wait(lock);
            std::cout << 0 << " ";
            ++count;
            if (count % 2 == 1)
            {
                lock.unlock();
                cv2.notify_one();
            }
            else
            {
                lock.unlock();
                cv3.notify_one();
            }
        }
    }
    
    void printodd(int end)
    {
        while (count <= end)
        {
            std::unique_lock<std::mutex> lock(m);
            cv2.wait(lock);
            if (count % 2 == 1)
            {
                std::cout << count << " ";
                ++count;
                lock.unlock();
                cv1.notify_one();
            }
        }
    }
    
    void printeven(int end)
    {
        while (count <= end)
        {
            std::unique_lock<std::mutex> lock(m);
            cv3.wait(lock);
            if (count % 2 == 0)
            {
                std::cout << count << " ";
                ++count;
                lock.unlock();
                cv1.notify_one();
            }
        }
    }
    
    int main()
    {
        int end = 10;
    
        std::thread t3(printzero, end);
        std::thread t1(printodd, end);
        std::thread t2(printeven, end);
    
        cv1.notify_one();
    
        t1.join();
        t2.join();
        t3.join();
    
        return 0;
    }
    

    我的解决方案似乎处于僵局。我什至不确定逻辑是否正确。请帮忙

    最佳答案

    您的代码有几个问题。为了使它起作用,您需要执行以下操作:

  • 修改您的while (count <= end)检查。在不同步的情况下读取count是未定义行为(UB)。
  • std::condition_variable::wait 使用适当的谓词。没有谓词的代码问题:
  • 如果在notify_one之前调用wait,则通知将丢失。在最坏的情况下,对mainnotify_one调用是在线程开始运行之前执行的。结果,所有线程可能会无限期地等待。
  • Spurious wakeups可能会破坏您的程序流程。另请参见cppreference.com on std::condition variable
  • 使用std::flush(请确保)。

  • 我玩了很多您的代码。在下面,您找到了我应用建议的修复程序的版本。此外,我还尝试了一些其他想法。
    #include <cassert>
    
    #include <condition_variable>
    #include <functional>
    #include <iostream>
    #include <mutex>
    #include <thread>
    #include <vector>
    
    // see the `std::mutex` for an example how to avoid global variables
    
    std::condition_variable cv_zero{};
    std::condition_variable cv_nonzero{};
    
    bool done = false;
    int next_digit = 1;
    bool need_zero = true;
    
    void print_zero(std::mutex& mt) {
      while(true) {// do not read shared state without holding a lock
        std::unique_lock<std::mutex> lk(mt);
        auto pred = [&] { return done || need_zero; };
        cv_zero.wait(lk, pred);
        if(done) break;
    
        std::cout << 0 << "\t"
                  << -1 << "\t"// prove that it works
                  << std::this_thread::get_id() << "\n"// prove that it works
                  << std::flush;
    
        need_zero = false;
    
        lk.unlock();
        cv_nonzero.notify_all();// Let the other threads decide which one
                                // wants to proceed. This is probably less
                                // efficient, but preferred for
                                // simplicity.
      }
    }
    
    void print_nonzero(std::mutex& mt, int end, int n, int N) {
    // Example for `n` and `N`: Launch `N == 2` threads with this
    // function. Then the thread with `n == 1` prints all odd numbers, and
    // the one with `n == 0` prints all even numbers.
      assert(N >= 1 && "number of 'nonzero' threads must be positive");
      assert(n >= 0 && n < N && "rank of this nonzero thread must be valid");
    
      while(true) {// do not read shared state without holding a lock
        std::unique_lock<std::mutex> lk(mt);
        auto pred = [&] { return done || (!need_zero && next_digit % N == n); };
        cv_nonzero.wait(lk, pred);
        if(done) break;
    
        std::cout << next_digit << "\t"
                  << n << "\t"// prove that it works
                  << std::this_thread::get_id() << "\n"// prove that it works
                  << std::flush;
    
    // Consider the edge case of `end == INT_MAX && next_digit == INT_MAX`.
    // -> You need to check *before* incrementing in order to avoid UB.
    
        assert(next_digit <= end);
        if(next_digit == end) {
          done = true;
          cv_zero.notify_all();
          cv_nonzero.notify_all();
          break;
        }
    
        ++next_digit;
        need_zero = true;
    
        lk.unlock();
        cv_zero.notify_one();
      }
    }
    
    int main() {
      int end = 10;
      int N = 2;// number of threads for `print_nonzero`
    
      std::mutex mt{};// example how to pass by reference (avoiding globals)
    
      std::thread t_zero(print_zero, std::ref(mt));
    
    // Create `N` `print_nonzero` threads with `n` in [0, `N`).
      std::vector<std::thread> ts_nonzero{};
      for(int n=0; n<N; ++n) {
    // Note that it is important to pass `n` by value.
        ts_nonzero.emplace_back(print_nonzero, std::ref(mt), end, n, N);
      }
    
      t_zero.join();
      for(auto&& t : ts_nonzero) {
        t.join();
      }
    }
    

    关于multithreading - 同步3个线程以打印顺序输出,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/56731861/

    相关文章:

    c++ - _InterlockedCompareExchange64的用法

    java - java多线程中如何锁定多个资源

    c++ - 仔细检查锁定问题,c++

    c++ - std::vector 初始化元素的 move/复制构造函数

    c++ - 线程是否共享一些类字段?

    c++ - 多线程给出了一些奇怪的结果

    c++ - 没有 typedef 的可变参数扩展器

    c++ - 遍历动态 vector 时 auto 的异常行为

    c++ - 为什么我的对象没有被插入到 std::set 中?

    C++11 for 模板函数中的循环