21xrx.com
2024-12-22 21:06:07 Sunday
登录
文章检索 我的文章 写文章
C++ 多线程编程实战
2023-06-22 01:24:43 深夜i     --     --
C++ 多线程编程 实战

C++是一门非常流行的编程语言,在许多领域都得到了广泛的应用,包括多线程编程。由于现代计算机都具备多核心的特性,多线程编程已经成为了极其重要的编程技能之一。本文将介绍C++多线程编程实战,以帮助开发者们更好地掌握这一技能。

1. 创建线程

多线程程序需要在进程中创建多个线程来并行执行多个任务。在C++中,创建线程的方法非常简单,只需要使用std::thread即可。下面是一个简单的示例:


#include <iostream>

#include <thread>

void foo() world!" << std::endl;

int main() {

  std::thread t1(foo);

  t1.join();

  return 0;

}

上述代码会在main函数中创建一个新线程,并将函数foo作为新线程的入口点。新线程开始执行后,会输出“Hello, world!”这句话。

2. 线程间通信

多线程之间需要通信,才能更好地协同工作。在C++中,线程间通信可以通过共享内存或消息队列来实现。共享内存指的是在不同线程之间共享同一份内存空间,而消息队列则是在不同线程之间传递消息。

对于共享内存,C++提供了std::atomic和std::mutex等机制来保证原子性和互斥性,避免多个线程同时读写同一份内存数据导致的竞态条件等问题。下面是一个std::mutex的示例:


#include <iostream>

#include <mutex>

#include <thread>

std::mutex mtx;

void setX(int& x, int value) {

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

  x = value;

}

int main() {

  int x = 0;

  std::thread t1(setX, std::ref(x), 1);

  std::thread t2(setX, std::ref(x), 2);

  t1.join();

  t2.join();

  std::cout << "x: " << x << std::endl;

  return 0;

}

上述代码中,setX函数会对传入的x变量进行赋值操作,而进入setX函数的前提是先获取到std::mutex的锁。由于std::mutex是可重入的,所以同一个线程可以多次获取锁而不会产生死锁。

除了共享内存,线程间通信还可以通过消息队列来实现。C++提供了std::condition_variable和std::future等机制来帮助开发者使用消息队列。下面是一个std::condition_variable的示例:


#include <iostream>

#include <condition_variable>

#include <thread>

std::condition_variable cv;

std::mutex mtx;

bool ready = false;

void foo() {

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

  while (!ready) {

    cv.wait(lock);

  }

  std::cout << "Hello, world!" << std::endl;

}

int main() {

  std::thread t1(foo);

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

  {

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

    ready = true;

  }

  cv.notify_one();

  t1.join();

  return 0;

}

上述代码中,foo函数会在准备好之后输出一句话。准备好的前提是通过std::condition_variable等待条件变量ready变成true。主函数中会在t1线程启动后1秒钟唤醒t1线程,并将ready变量设置为true。

3. 线程安全性

多线程编程需要考虑线程安全性问题,避免多个线程同时访问同一份数据导致的问题。在C++中,线程安全问题可以通过互斥访问和原子性访问来解决。

互斥访问指的是在访问共享资源的时候,通过互斥量保证只有一个线程访问共享资源,其他线程则等待。原子性访问则是指一次只有一个线程可以访问共享资源,避免了同时访问的问题。

对于互斥访问,C++提供了std::mutex、std::lock_guard等机制。对于原子性访问,C++提供了std::atomic等机制。下面是一个std::atomc的示例:


#include <iostream>

#include <atomic>

#include <vector>

#include <thread>

std::atomic<int> cnt = 0;

void increment() {

  ++cnt;

}

int main() {

  std::vector<std::thread> threads;

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

    threads.emplace_back(increment);

  }

  for (auto& t : threads) {

    t.join();

  }

  std::cout << "cnt: " << cnt << std::endl;

  return 0;

}

上述代码中,定义了一个std::atomic 类型的变量cnt,多个线程在执行increment函数时对cnt进行自增操作。由于std::atomic保证了对cnt的原子性操作,所以最终输出的cnt的值一定是10,而不会出现其他结果。

总结

C++多线程编程是一项非常重要的技能,需要注意线程间通信、线程安全性等问题。需要遵循一定的编程规范,尤其是避免竞态条件等问题。通过本文的讲解,希望能够帮助开发者们更好地掌握C++多线程编程实战。

  
  

评论区

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