21xrx.com
2024-09-20 08:46:41 Friday
登录
文章检索 我的文章 写文章
C++多线程同步技巧
2023-07-10 09:18:36 深夜i     --     --
C++多线程 同步技巧 互斥锁 条件变量 原子操作

C++中的多线程编程是一个需要注意同步问题的难点。当多个线程同时访问共享资源时,会出现数据竞争的情况,导致程序的行为出现不可预测的结果。为了保证程序的正确性和稳定性,在多线程编程时需要使用一些同步技巧来避免数据竞争。

1. 互斥锁

互斥锁是最基本的同步技巧之一。它是一种资源锁,用于防止多个线程同时访问同一个共享资源。当一个线程获取了互斥锁之后,其他线程就不能再获取该锁,直到该线程释放锁为止。

C++标准库中提供了std::mutex类来实现互斥锁,具体使用方法如下:


#include <iostream>

#include <mutex>

std::mutex mtx;

void shared_print(std::string msg, int id) {

  std::lock_guard<std::mutex> guard(mtx);

  std::cout << msg << id << std::endl;

}

void func(int id) {

  for(int i = 0; i < 10; ++i) {

    shared_print("Thread: ", id);

  }

}

int main() {

  std::thread t1(func, 1);

  std::thread t2(func, 2);

  t1.join();

  t2.join();

}

在上述代码中,我们定义了一个互斥锁对象`mtx`和一个输出函数`shared_print`。在函数`func`中,我们对共享资源(输出语句)加锁,避免了两个线程同时输出的情况。

2. 条件变量

条件变量是一种用于线程间通信的同步技巧。它可以让一个线程等待另一个线程满足某个条件后再继续运行。条件变量需要和互斥锁一起使用,来防止多个线程同时访问共享资源。

C++标准库中提供了std::condition_variable类来实现条件变量,具体使用方法如下:


#include <iostream>

#include <thread>

#include <mutex>

#include <condition_variable>

std::mutex mtx;

std::condition_variable cv;

bool ready = false;

void reader_thread() {

  std::unique_lock<std::mutex> lk(mtx);

  while(!ready) {

    cv.wait(lk);

  }

  std::cout << "Value: " << ready << std::endl;

}

void writer_thread() {

  std::unique_lock<std::mutex> lk(mtx);

  ready = true;

  cv.notify_one();

}

int main() {

  std::thread t1(reader_thread);

  std::thread t2(writer_thread);

  t1.join();

  t2.join();

}

在上述代码中,我们定义了一个互斥锁对象`mtx`、一个条件变量对象`cv`和一个标志变量`ready`。在读取线程中,我们通过条件变量的wait函数让线程等待写入线程操作完成;当写入线程完成操作后,我们通过条件变量的notify_one函数通知读取线程。

3. 原子操作

原子操作是一种基本的同步技巧,它可以保证对共享资源的操作是原子的,即不会被其他线程中断。在C++中,我们可以使用std::atomic类来实现原子操作。


#include <iostream>

#include <thread>

#include <atomic>

std::atomic<int> count(0);

void func(int n) {

  for(int i = 0; i < n; ++i) {

    ++count;

  }

}

int main() {

  std::thread t1(func, 1000000);

  std::thread t2(func, 1000000);

  t1.join();

  t2.join();

  std::cout << "Count: " << count << std::endl;

}

在上述代码中,我们定义了一个原子变量`count`,并在两个线程中增加其值。由于原子操作的特性,我们可以保证所有的加法操作都是原子的,避免了数据竞争的情况。

总的来说,多线程编程是一项复杂而又重要的任务,在使用多线程时需要注意同步问题,保证程序的正确性和稳定性。除了上面介绍的几种技巧外,还有其他许多同步技巧可供选择,开发者可以根据具体需求进行选择。

  
  

评论区

{{item['qq_nickname']}}
()
回复
回复