21xrx.com
2024-12-22 20:27:12 Sunday
登录
文章检索 我的文章 写文章
C++多线程编程实战指南
2023-07-10 02:02:38 深夜i     --     --
C++ 多线程编程 实战指南

随着计算机硬件的日益发展,多核处理器逐渐成为主流,多线程编程也越来越受到重视。C++作为一门经典的编程语言,其多线程编程模型也非常强大。本文介绍C++多线程编程的实战指南,帮助读者快速掌握多线程编程的基本方法和技巧。

1. 线程的创建和销毁

C++11标准引入了std::thread类,方便了线程的创建和管理。创建线程的基本方法如下:


std::thread t(func, args); //创建一个线程,并指定执行的函数func和参数args

其中,func是一个可调用对象,可以是函数指针、函数对象、Lambda表达式等。可以通过join()或detach()方法来等待或分离线程。


t.join(); //等待线程执行完毕

t.detach(); //将线程分离,使其在后台继续执行

2. 互斥锁与条件变量

在多线程编程中,为了防止多个线程同时读写共享资源导致数据的不一致性,需要使用互斥锁进行同步。C++11标准中引入了std::mutex类来实现互斥锁:


std::mutex mtx; //定义一个互斥锁

使用互斥锁的基本方法如下:


mtx.lock(); //获取锁

//访问共享资源

mtx.unlock(); //释放锁

除了互斥锁外,条件变量也是多线程编程中常用的同步机制。条件变量用于在一个线程等待另外一个线程满足某个条件时进行同步。C++11标准中引入了std::condition_variable类来实现条件变量:


std::condition_variable cv; //定义一个条件变量

使用条件变量的基本方法如下:


std::unique_lock<std::mutex> lk(mtx); //定义一个互斥锁的unique_lock对象

while (!condition) {

  cv.wait(lk); //等待条件变量满足

}

//do something

cv.notify_one(); //通知等待线程条件变量已经满足

3. 原子操作

在多线程编程中,为了防止读写共享资源时发生竞态条件,需要使用原子操作进行同步。C++11标准中引入了std::atomic类来实现原子操作:


std::atomic<int> num(0); //定义一个原子变量

使用原子操作的基本方法如下:


num++; //原子加1操作

num--; //原子减1操作

num.fetch_add(1); //原子加1操作,返回旧值

num.fetch_sub(1); //原子减1操作,返回旧值

4. 线程池

线程池是一种常见的多线程编程模型,用于管理线程的数量和有效利用CPU资源。C++标准库中没有提供线程池的实现,但可以自己实现一个简单的线程池:


class ThreadPool {

public:

  ThreadPool(size_t threads) : stop(false) {

    for (size_t i = 0; i < threads; ++i) {

      workers.emplace_back(

        [this] {

          for (;;) {

            std::function<void()> task;

            {

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

              while (!stop && tasks.empty()) {

                condition.wait(lock);

              }

              if (stop && tasks.empty())

                return;

              

              task = std::move(tasks.front());

              tasks.pop();

            }

            task();

          }

        }

      );

    }

  }

  template<class F>

  void enqueue(F&& f) {

    {

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

      tasks.emplace(std::forward<F>(f));

    }

    condition.notify_one();

  }

  ~ThreadPool() {

    {

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

      stop = true;

    }

    condition.notify_all();

    for (std::thread& worker : workers) {

      worker.join();

    }

  }

private:

  std::vector<std::thread> workers;

  std::queue<std::function<void()>> tasks;

  std::mutex queue_mutex;

  std::condition_variable condition;

  bool stop;

};

5. 多线程编程实例

下面给出一个简单的多线程编程实例,用于计算数组中所有元素的平均值:


#include <iostream>

#include <vector>

#include <thread>

#include <numeric>

double avg(std::vector<double>::iterator begin, std::vector<double>::iterator end) {

  double sum = std::accumulate(begin, end, 0.0);

  double size = end - begin;

  return sum / size;

}

double parallel_avg(std::vector<double>& data) {

  size_t threads = std::thread::hardware_concurrency();

  std::vector<std::thread> workers;

  std::vector<double> results(threads);

  size_t block_size = data.size() / threads;

  auto begin = data.begin();

  for (size_t i = 0; i < threads - 1; ++i) {

    auto end = begin;

    std::advance(end, block_size);

    workers.emplace_back([&results, begin, end, i] {

      results[i] = avg(begin, end);

    });

    begin = end;

  }

  results[threads - 1] = avg(begin, data.end());

  for (auto& worker : workers) {

    worker.join();

  }

  return avg(results.begin(), results.end());

}

int main() {

  std::vector<double> data(1000000, 1.0);

  std::cout << parallel_avg(data) << std::endl;

  return 0;

}

通过创建多个线程,将数组分块计算平均值,并将每个线程的结果汇总得到数组的平均值。在本地机器上测试,可以得到比单线程计算快了约2倍的速度提升。

总结

本文介绍了C++多线程编程的实战指南,包括线程的创建和销毁、互斥锁与条件变量、原子操作、线程池以及一个简单的多线程编程实例。多线程编程是现代编程不可或缺的一部分,掌握多线程编程的方法和技巧对于提高程序的性能具有重要意义。

  
  

评论区

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