C++11多线程:并发编程、异步任务、原子操作、锁机制和线程池

C++11引入了许多新的特性,其中包括对多线程编程的支持。本文将介绍C++11中多线程编程的各种方面,包括并发编程、异步任务、原子操作、锁机制以及线程池等。

一、并发编程

并发编程是指在一个应用程序中同时执行多个任务的能力。多线程是实现并发编程的一种常见方式。C++11为多线程编程提供了一组重要的工具,包括线程、互斥锁、条件变量、原子操作和同步队列。下面分别介绍这些内容。

1. 线程

线程是指进程中执行的并发任务。C++11中的线程类是std::thread,使用它可以创建和控制线程。下面是一个简单的线程示例,展示了如何创建和启动一个新的线程。

#include 
#include 

void thread_function()
{
   std::cout << "thread function running" << std::endl;
}

int main()
{
   std::thread t(&thread_function);
   std::cout << "main thread running" << std::endl;
   t.join();
   return 0;
}

在上面的示例中,std::thread对象t是一个新线程,它的构造函数接受线程函数和函数参数。在这个示例中,线程函数是thread_function(),没有参数。线程对象t被创建后,可以调用它的join()方法来等待线程完成。

2. 互斥锁和条件变量

在多线程程序中,对共享数据的并发访问可能会导致问题。为了避免这些问题,C++11提供了互斥锁和条件变量。互斥锁可以用于同步多个线程之间的访问,条件变量可以用于在线程之间传递信号。下面是使用互斥锁和条件变量的示例:

#include 
#include 
#include 
#include 
#include 

std::mutex mtx;
std::condition_variable cv;
std::queue q;
bool ready = false;

void producer_thread()
{
   for (int i = 0; i < 5; ++i) {
      std::unique_lock lock(mtx);
      q.push(i);
      std::cout << "producer thread pushed " << i << std::endl;
      ready = true;
      cv.notify_one();
   }
}

void consumer_thread()
{
   while (true) {
      std::unique_lock lock(mtx);
      while (!ready) cv.wait(lock);
      if (q.empty()) break;
      int data = q.front();
      q.pop();
      std::cout << "consumer thread got " << data << std::endl;
      ready = false;
   }
}

int main()
{
   std::thread producer(producer_thread);
   std::thread consumer(consumer_thread);
   producer.join();
   consumer.join();
   return 0;
}

在上面的示例中,我们使用了互斥锁和条件变量来同步生产者和消费者线程。生产者线程调用cv.notify_one()来通知消费者线程可以取数据了,消费者线程使用cv.wait(lock)来等待生产者线程的通知。

二、异步任务

异步任务是指程序中可以单独执行、不阻塞主线程的任务。C++11为异步任务提供了一个很方便的方式:std::async()。使用std::async()可以在新的线程中异步执行一个函数,同时返回一个std::future对象,该对象可以用于获取函数的返回值或者等待函数执行完成。下面是std::async()的一个示例:

#include 
#include 

int async_function()
{
   std::cout << "async_function running" << std::endl;
   return 42;
}

int main()
{
   std::future f = std::async(std::launch::async, &async_function);
   std::cout << "main thread running" << std::endl;
   std::cout << "async_function returned " << f.get() << std::endl;
   return 0;
}

在上面的示例中,std::async()函数被用来异步执行函数async_function()。该函数返回一个std::future对象f,用于获取异步函数的返回值。在主函数中,我们使用f.get()方法来等待异步函数执行完成并且获取其返回值。

三、原子操作和锁机制

原子操作是指可以在没有锁的情况下对共享数据进行读写操作的语句。C++11中提供了std::atomic类来支持原子操作。std::atomic类可以确保任何对原子对象的操作都是对整个值进行原子化的,因此不会发生竞争条件。下面是一个原子操作的示例:

#include 
#include 
#include 

std::atomic counter(0);

void thread_function()
{
   for (int i = 0; i < 10000; ++i) {
      counter++;
   }
}

int main()
{
   std::thread t1(thread_function);
   std::thread t2(thread_function);
   t1.join();
   t2.join();
   std::cout << "counter = " << counter << std::endl;
   return 0;
}

上面的示例中,我们使用std::atomic来定义了一个原子化的计数器。在两个线程中分别对其进行了10000次自增操作。由于使用了std::atomic,所以两个线程对计数器的自增操作是原子化的,不会导致数据竞争。最终输出的counter的值为20000。

锁机制也是避免多线程访问共享数据时出现数据竞争的一种重要手段。C++11提供了多种类型的锁来支持多线程程序。下面是使用std::mutex进行线程同步的示例:

#include 
#include 
#include 

std::mutex mtx;
int counter = 0;

void thread_function()
{
   for (int i = 0; i < 10000; ++i) {
      std::unique_lock lock(mtx);
      counter++;
   }
}

int main()
{
   std::thread t1(thread_function);
   std::thread t2(thread_function);
   t1.join();
   t2.join();
   std::cout << "counter = " << counter << std::endl;
   return 0;
}

在上面的示例中,我们使用std::mutex来定义了一个互斥锁。在两个线程中对计数器进行自增前,先进行了加锁操作,以保证操作的原子性和线程安全。最终计数器的值也是20000。

四、线程池

线程池是一种常见的并发编程技术,通过维护一组线程,以待执行的任务队列作为输入,执行任务队列中的任务,以实现多线程并发执行。C++11中并没有提供线程池的实现,但我们可以自己实现一个简单的线程池。下面是一个线程池的示例:

#include 
#include 
#include 
#include 
#include 

class ThreadPool
{
public:
   ThreadPool(std::size_t n) : stopped(false)
   {
      for (std::size_t i = 0; i < n; ++i) {
         workers.emplace_back(
            [this] {
               for (;;) {
                  std::function task;
                  {
                     std::unique_lock lock(queue_mutex);
                     condition.wait(lock, [this]() { return stopped || !tasks.empty(); });
                     if (stopped && tasks.empty()) return;
                     task = std::move(tasks.front());
                     tasks.pop();
                  }
                  task();
               }
            }
         );
      }
   }

   template 
   auto enqueue(F&& f, Args&&... args) -> std::future<typename std::result_of::type>
   {
      using return_type = typename std::result_of::type;

      auto task = std::make_shared<std::packaged_task>(std::bind(std::forward(f), std::forward(args)...));

      std::future res = task->get_future();
      {
         std::unique_lock lock(queue_mutex);
         if (stopped) throw std::runtime_error("enqueue on stopped ThreadPool");
         tasks.emplace([task]() { (*task)(); });
      }
      condition.notify_one();
      return res;
   }

   ~ThreadPool()
   {
      {
         std::unique_lock lock(queue_mutex);
         stopped = true;
      }
      condition.notify_all();
      for (std::thread& worker : workers)
         worker.join();
   }

private:
   std::vector workers;
   std::queue<std::function> tasks;
   std::mutex queue_mutex;
   std::condition_variable condition;
   bool stopped;
};

int main()
{
   ThreadPool pool(4);
   std::vector<std::future> results;

   for (int i = 0; i  int {
            std::cout << "task " << i << " running" << std::endl;
            return i * i;
         }, i)
      );
   }

   for (std::future& result : results) {
      std::cout << "result = " << result.get() << std::endl;
   }

   return 0;
}

在上面的示例中,我们定义了一个ThreadPool类,它维护了一组线程和一个任务队列。线程每次从任务队列中取出一个任务执行,直到任务队列为空或者被告知停止。ThreadPool类提供了enqueue()函数来向任务队列中添加任务,enqueue()函数返回一个std::future对象,用于获取任务的返回值。

结论

本文介绍了C++11中多线程编程的各种方面,包括并发编程、异步任务、原子操作、锁机制以及线程池等。多线程编程是一项复杂的任务,需要仔细考虑应用程序的数据共享和线程同步问题。通过了解C++11多线程编程提供的工具和技术,可以使多线程编程更加容易和高效。

原创文章,作者:DISNI,如若转载,请注明出处:https://www.506064.com/n/329672.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
DISNIDISNI
上一篇 2025-01-14 18:55
下一篇 2025-01-14 18:55

相关推荐

  • Python多线程读取数据

    本文将详细介绍多线程读取数据在Python中的实现方法以及相关知识点。 一、线程和多线程 线程是操作系统调度的最小单位。单线程程序只有一个线程,按照程序从上到下的顺序逐行执行。而多…

    编程 2025-04-29
  • Python线程等待指南

    本文将从多个方面详细讲解Python线程等待的相关知识。 一、等待线程结束 在多线程编程中,经常需要等待线程执行完毕再进行下一步操作。可以使用join()方法实现等待线程执行完毕再…

    编程 2025-04-29
  • Python两个线程交替打印1到100

    这篇文章的主题是关于Python多线程的应用。我们将会通过实际的代码,学习如何使用Python两个线程交替打印1到100。 一、创建线程 在Python中,我们可以使用Thread…

    编程 2025-04-28
  • ROS线程发布消息异常解决方法

    针对ROS线程发布消息异常问题,我们可以从以下几个方面进行分析和解决。 一、检查ROS代码是否正确 首先,我们需要检查ROS代码是否正确。可能会出现的问题包括: 是否正确初始化RO…

    编程 2025-04-28
  • Spring S_CSRF防护机制实现及应用

    Spring S_CSRF防护机制是Spring Security框架提供的一个针对跨站请求伪造攻击(CSRF)的保护机制。本文将从以下几个方面详细介绍Spring S_CSRF防…

    编程 2025-04-28
  • Python线程池并发爬虫

    Python线程池并发爬虫是实现多线程爬取数据的常用技术之一,可以在一定程度上提高爬取效率和数据处理能力。本文将从多个方面对Python线程池并发爬虫做详细的阐述,包括线程池的实现…

    编程 2025-04-27
  • 多线程和多进程的应用

    多线程和多进程是现代编程中常用的技术,可以提高程序的效率和性能。本文将从不同的角度对多线程和多进程进行详细的介绍和应用。 一、多线程 vs 多进程 多线程和多进程都是为了实现程序并…

    编程 2025-04-27
  • Python的垃圾回收机制

    本文将对Python的垃圾回收机制进行详细阐述,着重介绍它的基本原理和实现方式。此外,我们还将介绍常见的问题及解决方法,并给出相应的代码示例。 一、Python的垃圾回收概述 垃圾…

    编程 2025-04-27
  • Python多线程模块实践

    本文将向大家介绍Python中的多线程模块,并通过示例代码来展示如何灵活使用线程提升程序的性能。同时,本文还将讨论Python多线程模块使用中可能遇到的一些问题及其解决方法。 一、…

    编程 2025-04-27
  • 线程池中的一个线程异常了会被怎么处理

    本文将从以下几个方面对线程池中的一个线程异常了会被怎么处理进行详细阐述:异常的类型、如何捕获异常、异常的处理方式。 一、异常的类型 在线程池中,可以出现多种类型的异常,例如线程执行…

    编程 2025-04-27

发表回复

登录后才能评论