面试题:C++ 多线程开发需要注意些什么?线程同步有哪些手段?

C++ 多线程开发是一个复杂且容易出错的领域,需要特别注意线程安全、资源竞争、死锁等问题。以下是多线程开发中需要注意的事项以及常用的线程同步手段。


C++ 多线程开发需要注意的事项

  1. 线程安全
    • 确保多个线程同时访问共享资源时不会导致数据竞争(Data Race)或未定义行为。
    • 使用同步机制(如互斥锁、原子操作)保护共享资源。
  2. 资源竞争
    • 多个线程同时访问共享资源(如全局变量、堆内存、文件等)可能导致数据不一致。
    • 通过加锁或使用无锁数据结构避免资源竞争。
  3. 死锁
    • 当多个线程互相等待对方释放锁时,会导致死锁。
    • 避免死锁的方法:
      • 按固定顺序加锁。
      • 使用 std::lock 或 std::scoped_lock 一次性获取多个锁。
      • 设置超时机制。
  4. 性能问题
    • 过多的锁竞争会导致性能下降。
    • 尽量减少锁的粒度,避免长时间持有锁。
    • 使用无锁编程(Lock-Free Programming)或线程本地存储(Thread Local Storage, TLS)来减少锁的使用。
  5. 线程生命周期管理
    • 确保线程在完成任务后正确退出,避免资源泄漏。
    • 使用 std::thread::join() 或 std::thread::detach() 管理线程的生命周期。
  6. 异常处理
    • 线程中的异常如果没有捕获,会导致程序崩溃。
    • 在线程入口函数中使用 try-catch 块捕获异常。
  7. 虚假唤醒
    • 使用条件变量时,线程可能会在没有收到通知的情况下被唤醒(虚假唤醒)。
    • 解决方法是使用 while 循环检查条件,而不是 if

线程同步的手段

  1. 互斥锁(Mutex)
    • 用于保护共享资源,确保同一时间只有一个线程可以访问。
    • C++ 提供了 std::mutexstd::recursive_mutexstd::timed_mutex 等。
    • 示例:
      #include <iostream>
      #include <thread>
      #include <mutex>
      
      std::mutex mtx;
      int shared_data = 0;
      
      void increment() {
          std::lock_guard<std::mutex> lock(mtx);
          ++shared_data;
      }
      
      int main() {
          std::thread t1(increment);
          std::thread t2(increment);
          t1.join();
          t2.join();
          std::cout << "Shared data: " << shared_data << std::endl;
          return 0;
      }
  2. 条件变量(Condition Variable)
    • 用于线程间的通信,允许线程等待某个条件成立。
    • 通常与互斥锁一起使用。
    • 示例:
      #include <iostream>
      #include <thread>
      #include <mutex>
      #include <condition_variable>
      
      std::mutex mtx;
      std::condition_variable cv;
      bool ready = false;
      
      void wait_for_ready() {
          std::unique_lock<std::mutex> lock(mtx);
          cv.wait(lock, []{ return ready; });
          std::cout << "Ready!" << std::endl;
      }
      
      void set_ready() {
          std::this_thread::sleep_for(std::chrono::seconds(1));
          {
              std::lock_guard<std::mutex> lock(mtx);
              ready = true;
          }
          cv.notify_all();
      }
      
      int main() {
          std::thread t1(wait_for_ready);
          std::thread t2(set_ready);
          t1.join();
          t2.join();
          return 0;
      }
  3. 原子操作(Atomic)
    • 用于无锁编程,确保对共享变量的操作是原子的。
    • C++ 提供了 std::atomic 模板类。
    • 示例:
      #include <iostream>
      #include <thread>
      #include <atomic>
      
      std::atomic<int> counter(0);
      
      void increment() {
          for (int i = 0; i < 1000; ++i) {
              ++counter;
          }
      }
      
      int main() {
          std::thread t1(increment);
          std::thread t2(increment);
          t1.join();
          t2.join();
          std::cout << "Counter: " << counter << std::endl;
          return 0;
      }
  4. 读写锁(Read-Write Lock)
    • 允许多个线程同时读取共享资源,但写操作是独占的。
    • C++17 提供了 std::shared_mutex
    • 示例:
      #include <iostream>
      #include <thread>
      #include <shared_mutex>
      
      std::shared_mutex rw_mutex;
      int shared_data = 0;
      
      void read_data() {
          std::shared_lock<std::shared_mutex> lock(rw_mutex);
          std::cout << "Read data: " << shared_data << std::endl;
      }
      
      void write_data() {
          std::unique_lock<std::shared_mutex> lock(rw_mutex);
          ++shared_data;
          std::cout << "Write data: " << shared_data << std::endl;
      }
      
      int main() {
          std::thread t1(read_data);
          std::thread t2(write_data);
          t1.join();
          t2.join();
          return 0;
      }
  5. 信号量(Semaphore)
    • 用于控制对共享资源的访问数量。
    • C++20 提供了 std::counting_semaphore
    • 示例:
      #include <iostream>
      #include <thread>
      #include <semaphore>
      
      std::counting_semaphore<1> semaphore(1);
      
      void task() {
          semaphore.acquire();
          std::cout << "Task running" << std::endl;
          std::this_thread::sleep_for(std::chrono::seconds(1));
          semaphore.release();
      }
      
      int main() {
          std::thread t1(task);
          std::thread t2(task);
          t1.join();
          t2.join();
          return 0;
      }

总结

  • 注意事项:线程安全、资源竞争、死锁、性能、线程生命周期管理、异常处理、虚假唤醒。
  • 同步手段:互斥锁、条件变量、原子操作、读写锁、信号量。
  • 在多线程开发中,合理选择同步机制并遵循最佳实践,可以避免大多数并发问题。
THE END
点赞14 分享
评论 抢沙发
头像
欢迎您留下宝贵的见解!
提交
头像

昵称

取消
昵称表情代码图片

    暂无评论内容