21xrx.com
2024-11-24 16:13:23 Sunday
登录
文章检索 我的文章 写文章
C++多线程通信详解
2023-07-12 12:56:40 深夜i     --     --
C++ 多线程 通信

C++中多线程编程是非常常见的,具有很高的效率和灵活性。但是,在多线程编程中,不同线程之间如何通信是非常重要的问题。本文将介绍C++多线程通信的详细方法。

1.共享变量

在多线程编程中,共享变量是最常见的通信方式。共享变量需要被所有线程所共享,因此需要使用互斥量(mutex)来保护多线程对共享变量的读写。

使用互斥量来保护共享变量的读写:


#include <mutex>

#include <iostream>

using namespace std;

int count = 0;    //共享变量

std::mutex mtx;    //互斥量

int main ()

{

  std::thread t1([](){

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

      mtx.lock();

      cout << "Thread 1: count = " << ++count << endl;  //线程1对共享变量进行读写

      mtx.unlock();

    }

  });

  std::thread t2([](){

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

      mtx.lock();

      cout << "Thread 2: count = " << ++count << endl;  //线程2对共享变量进行读写

      mtx.unlock();

    }

  });

  t1.join();

  t2.join();

  return 0;

}

以上代码中,我们定义了一个共享变量count,并创建了两个线程t1和t2,它们都对共享变量进行读写。互斥量的作用就是确保每个线程对共享变量的读写是排他的,避免出现数据不一致问题。

2.信号量

信号量(semaphore)是另一种常见的多线程通信方式。信号量可以用于控制多个线程之间的执行顺序和同步。

在C++中,可以使用std::semaphore来实现信号量:


#include <semaphore>

#include <iostream>

#include <chrono>

using namespace std;

std::binary_semaphore sem(0);  //创建一个二元信号量

int main()

{

  std::thread t1([&]() {

    cout << "Thread 1 is running." << endl;

    std::this_thread::sleep_for(std::chrono::seconds(1));

    sem.release();      //释放信号量

  });

  std::thread t2([&]() {

    sem.acquire();      //等待信号量被释放

    cout << "Thread 2 is running." << endl;

  });

  t1.join();

  t2.join();

  return 0;

}

以上代码中,我们创建了一个二元信号量sem,t1线程先运行并在1秒后释放信号量,t2线程等待信号量被释放后再运行。

3.条件变量

条件变量(condition_variable)是C++多线程编程中另一种常用的通信方式。条件变量可以使一个或多个线程等待某种条件成立,当条件成立时,线程可以被唤醒并执行相应的操作。

在C++中,条件变量可以使用std::condition_variable来创建:


#include <condition_variable>

#include <mutex>

#include <thread>

#include <iostream>

using namespace std;

std::mutex mtx;

std::condition_variable cv;

bool ready = false;

int main() {

  std::thread t1([&]() {

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

    while (!ready)       //等待条件成立

      cv.wait(lock);     //条件变量等待

    cout << "Thread 1: ready." << endl;

  });

  std::thread t2([&]() {

    std::this_thread::sleep_for(std::chrono::seconds(1));

    {

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

      ready = true;      //设置条件为true

    }

    cv.notify_one();      //通知条件变量

    cout << "Thread 2: set ready." << endl;

  });

  t1.join();

  t2.join();

  return 0;

}

以上代码中,我们使用条件变量来实现线程t1等待条件ready为true的情况。线程t2在1秒后将ready设置为true,并通过cv.notify_one()通知条件变量。当条件ready被设置为true时,线程t1被唤醒。

4.忙等待

忙等待(busy waiting)是一种低效的通信方式,但是在特定情况下仍然有用。当共享变量的读写非常频繁,且操作的时间非常短时,忙等待可以减少线程切换的开销,提高系统效率。例如:


#include <atomic>

#include <iostream>

using namespace std;

std::atomic<int> count(0);  //原子变量

int main () {

  std::thread t1([](){

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

      while (count.load(std::memory_order_relaxed) != 0) {}

      count.store(1, std::memory_order_release);  //设置共享变量

    }

  });

  std::thread t2([](){

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

      while (count.load(std::memory_order_relaxed) != 1) {}

      count.store(0, std::memory_order_release);  //设置共享变量

    }

  });

  t1.join();

  t2.join();

  return 0;

}

以上代码中,我们使用std::atomic来创建原子变量count。t1线程和t2线程交替执行,每次只有一个线程可以进入临界区(共享变量的读写部分)。当一个线程将count设置为1时,另一个线程等待count变为1,直到获得临界区的访问权限。

总结:

本文介绍了C++多线程通信的四种常见方式:共享变量、信号量、条件变量和忙等待。在多线程编程中,掌握合适的通信方式可以有效提高系统的效率和灵活性,值得广大开发者深入理解和实践。

  
  

评论区

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