21xrx.com
2024-11-22 08:00:38 Friday
登录
文章检索 我的文章 写文章
C++多线程编程思想:从基础概念到高级应用
2023-07-01 05:36:44 深夜i     --     --
C++ 多线程编程 基础概念 高级应用 思想
return this->stop || !this->tasks.empty();

随着计算机硬件的不断更新换代,每个CPU核心的计算能力不断提升,而且现代计算机通常都具备拥有多个CPU核心,这些核心能够并行处理各自的任务,从而提高计算机的整体性能。为了充分利用这些硬件资源,我们需要使用多线程编程技术。

C ++是一门经典的编程语言,也是多线程编程的重要工具。C ++的多线程编程涉及许多概念和技术,从基础的线程创建和同步到高级的线程池和异步编程,这里将介绍C ++多线程编程思想的基础概念和高级应用。

1.基础概念

线程是进程的一个执行流程,拥有独立的程序计数器、堆栈和局部变量,但共享进程的资源,如全局变量和内存。线程可以在同一个程序中并行运行,从而提高程序的性能。

C++的多线程编程需要使用标准库的 头文件。它提供了一些类和函数,用于创建、控制和管理线程。在C++中,创建线程最常见的方法是使用线程函数或Lambda表达式。

线程函数是一个普通的C++函数,它被执行在独立的线程中。要创建一个线程,可以通过std::thread类实例化一个对象,该对象的构造函数需要接收一个函数指针作为参数,该函数指针指向线程函数。例如:


void threadFunc()

{

  cout << "Hello from thread!\n";

}

int main()

{

  std::thread t(threadFunc);

  t.join();

  return 0;

}

这里,我们定义了一个名为threadFunc的函数,负责在线程中输出一条信息。然后,我们使用std::thread类的构造函数创建一个名为t的线程对象,并将threadFunc函数作为参数传递给它。最后,我们使用t.join()等待线程结束。

在C ++中,还可以使用Lambda表达式创建线程。Lambda表达式是一个匿名函数对象,它可以在运行时创建,而无需使用函数名。示例代码如下:


int main()

{

  std::thread t([](){

    cout << "Hello from thread!\n";

  });

  t.join();

  return 0;

}

这里,我们定义了一个Lambda表达式,它与线程函数的作用相同。创建线程时,我们将Lambda表达式作为参数传递给std::thread的构造函数。

2.线程同步和互斥

在多线程编程中,线程同步和互斥是极为重要的概念。当多个线程同时访问共享资源时,往往会导致数据竞争和不可测结果,因此我们需要使用同步和互斥机制来避免这种情况。

C++提供了一个名为std::mutex的类,用于在多个线程之间实现互斥锁。mutex对象提供了两个基本的操作:lock()和unlock()。lock()函数可以占用互斥锁,以保护共享资源。例如:


std::mutex mtx;

void threadFunc()

{

  mtx.lock();

  cout << "Hello from thread!\n";

  mtx.unlock();

}

int main()

{

  std::thread t(threadFunc);

  mtx.lock();

  cout << "Hello from main thread!\n";

  mtx.unlock();

  t.join();

  return 0;

}

在这个例子中,我们定义了一个mutex对象,负责保护共享资源。线程函数threadFunc会先占用该互斥锁,然后输出一条信息。在主线程中,我们也占用了该锁并输出一条信息。由于该锁是互斥的,因此只会有一个线程能够占用它。最后,我们等待线程结束并释放互斥锁。

3.高级应用

线程池是一种高级的多线程编程技术,它用于以一定数量的线程来处理一个队列中的任务。线程池可以提高程序的运行效率,因为线程池中的线程可以在处理一个任务时,同时等待下一个任务,从而避免了创建和销毁线程的开销。

C++中线程池的实现需要使用 头文件。下面是一个简单的示例代码:


class ThreadPool

{

public:

  ThreadPool(const int numThreads) : stop(false)

  {

    for (int i = 0; i < numThreads; ++i)

      workers.emplace_back([this] {

        for (;;) {

          std::function<void()> task;

          {

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

            this->cond.wait(lock, [this] { return this->stop || !this->tasks.empty(); });

            if (this->stop && this->tasks.empty()) return;

            task = std::move(this->tasks.front());

            this->tasks.pop();

          }

          task();

        }

      });

  }

  template<class Functor>

  void enqueue(Functor f)

  {

    {

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

      tasks.emplace(std::function<void()>(f));

    }

    cond.notify_one();

  }

  ~ThreadPool()

  {

    {

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

      stop = true;

    }

    cond.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 cond;

  bool stop;

};

这里,我们定义了一个名为ThreadPool的类,用于实现线程池。构造函数创建一定数量的线程,其中每个线程都会调用lambda表达式处理任务队列。enqueue函数将一个任务放入队列中。析构函数会销毁线程并等待它们结束。

4.总结

C++的多线程编程是一个非常有趣和复杂的主题。与其他编程语言相比,C ++具有更大的灵活性和可控性,因此在编写高性能甚至高并发的应用程序时,C ++往往是首选。无论是基础概念还是高级应用,掌握C++多线程编程思想都是一项必不可少的技能。

  
  

评论区

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