返回顶部
首页 > 资讯 > 后端开发 > 其他教程 >C++中线程的原理与实现方法是什么
  • 377
分享到

C++中线程的原理与实现方法是什么

2023-07-05 19:07:58 377人浏览 独家记忆
摘要

这篇文章主要介绍“c++中线程的原理与实现方法是什么”的相关知识,小编通过实际案例向大家展示操作过程,操作方法简单快捷,实用性强,希望这篇“C++中线程的原理与实现方法是什么”文章能帮助大家解决问题。在C++中有多种实现线程的方式C++11

这篇文章主要介绍“c++线程的原理与实现方法是什么”的相关知识,小编通过实际案例向大家展示操作过程,操作方法简单快捷,实用性强,希望这篇“C++中线程的原理与实现方法是什么”文章能帮助大家解决问题。

在C++中有多种实现线程的方式

  • C++11提供的标准多线程方式;

  • 第三方库(如:Boost.Thread);

  • 操作系统提供的多线程(如:windows 线程 与 POSIX 线程(pthread))。

我们这里先了解的就是C++11提供的标准多线程方式。因为它提供了良好的跨平台兼容性和简洁的语法,已经满足大多数需求。

从最简单的开始

C++11 引入了多线程支持,提供了一套基本的线程库,包括线程、互斥量(mutex)、条件变量(condition_variable)等。这些组件可以帮助你在 C++ 程序中实现并发和多线程编程。下面是一些基本概念和示例:

std::thread:

std::thread 是 C++11 中的线程类,用于创建和管理线程。您可以将一个函数作为参数传递给 std::thread 的构造函数,该函数将在新线程中执行。

#include <iOStream>#include <thread>void hello() {    std::cout << "Hello from thread!" << std::endl;}int main() {    std::thread t(hello); // 创建一个新线程,执行 hello 函数    t.join(); // 等待线程结束    return 0;}

std::mutex:

std::mutex 是互斥量类,用于保护共享资源的访问。当多个线程需要访问共享资源时,使用互斥量可以确保每次只有一个线程访问资源,从而避免数据竞争和其他并发问题。

#include <iostream>#include <mutex>#include <thread>std::mutex mtx; // 互斥量void print_block(int n, char c) {    mtx.lock(); // 定互斥量    for (int i = 0; i < n; ++i) {        std::cout << c;    }    std::cout << std::endl;    mtx.unlock(); // 解锁互斥量}int main() {    std::thread t1(print_block, 50, '*');    std::thread t2(print_block, 50, '$');    t1.join();    t2.join();    return 0;}

std::lock_guard:

std::lock_guard 是一个 RAII(Resource Acquisition Is Initialization)包装类,用于自动管理互斥量的锁定和解锁。当创建 std::lock_guard 对象时,它将自动锁定互斥量,当对象销毁时,它将自动解锁互斥量。

void print_block(int n, char c) {    std::lock_guard<std::mutex> lock(mtx); // 自动锁定互斥量    for (int i = 0; i < n; ++i) {        std::cout << c;    }    std::cout << std::endl;    // 自动解锁互斥量(lock_guard 对象销毁时)}

std::condition_variable:

std::condition_variable 是一个条件变量类,用于在线程之间同步操作。它可以与 std::mutex 配合使用,实现线程间的等待和通知机制。

#include <iostream>#include <condition_variable>#include <mutex>#include <thread>std::mutex mtx;std::condition_variable cv;bool ready = false;void print_id(int id) {    std::unique_lock<std::mutex> lck(mtx);     cv.wait(lck, [] { return ready; }); // 等待 ready 变为 true     std::cout << "thread " << id << std::endl; } void Go() {     std::unique_lock<std::mutex> lck(mtx);    ready = true;     cv.notify_all();// 通知所有等待的线程 }int main() {    std::thread threads[10];    for (int i = 0; i < 10; ++i) {        threads[i] = std::thread(print_id, i); // 启动 10 个线程     }        go(); // 通知所有线程开始执行    for (auto& th : threads) {        th.join();     }    return 0;}

在这个示例中,我们创建了 10 个线程,每个线程在启动后等待一个条件变量。主线程通过调用 go 函数将条件变量的状态设置为 true 并通知所有等待的线程,使它们开始执行。

std::future 和 std::async:

std::futurestd::async 是 C++11 提供的用于异步操作的类。std::async 可以异步地执行一个函数,并返回一个 std::future 对象,该对象表示该函数的返回值。您可以通过调用 std::future::get() 来等待函数执行完成并获取其返回值。

#include <iostream>#include <future>int sum(int a, int b) {    return a + b;}int main() {    std::future<int> result = std::async(sum, 10, 20); // 异步执行 sum 函数    int value = result.get(); // 等待执行完成并获取返回值    std::cout << "The result is: " << value << std::endl;    return 0;}

这个简单的示例展示了如何使用 std::async 异步地执行一个求和函数,然后通过std::future 获取其结果。

C++11 的多线程支持功能使得在 C++ 中实现并发编程变得更加简单。通过这些基本组件,您可以根据需要构建更复杂的并发程序。

C++11的线程只有这么简单吗?

是也不是。C++11 中的多线程库确实相对简单,但这只是表面现象。实际上,它们为复杂的多线程程序提供了基础。前面已经介绍了一些基本的多线程组件,例如 std::threadstd::mutexstd::condition_variablestd::futurestd::async。但是,还有一些其他的组件和技巧可能会对你有帮助:

std::atomic:

C++11 引入了原子类型(std::atomic),用于实现原子操作,即在多线程环境中不会被中断的操作。原子类型在多线程中特别有用,因为它们可以避免数据竞争和其他并发问题。

示例:

#include <iostream>#include <atomic>#include <thread>std::atomic<int> counter(0);void increase_counter() {    for (int i = 0; i < 1000; ++i) {        ++counter;    }}int main() {    std::thread t1(increase_counter);    std::thread t2(increase_counter);    t1.join();    t2.join();    std::cout << "Counter: " << counter << std::endl;    return 0;}

std::call_once:

std::call_once 是一个用于确保在多线程环境中某个函数只被调用一次的工具。它需要一个 std::once_flag 变量作为参数,该变量用于跟踪函数是否已被调用。

示例:

#include <iostream>#include <mutex>#include <thread>std::once_flag flag;void do_something() {    std::cout << "Called once" << std::endl;}void call_do_something() {    std::call_once(flag, do_something);}int main() {    std::thread t1(call_do_something);    std::thread t2(call_do_something);    t1.join();    t2.join();    return 0;}

线程局部存储:

C++11 支持线程局部存储,即每个线程拥有自己的变量副本。使用 thread_local 关键字可以定义一个线程局部变量。这对于某些需要每个线程拥有独立状态的应用场景非常有用。

示例:

#include <iostream>#include <thread>thread_local int counter = 0;void increase_counter() {    ++counter;    std::cout << "Counter: " << counter << " in thread " << std::this_thread::get_id() << std::endl;}int main() {    std::thread t1(increase_counter);    std::thread t2(increase_counter);    t1.join();    t2.join();    return 0;}

以上是 C++11 多线程库中的一些其他组件和技巧。虽然这些组件相对简单,但它们为实现复杂的多线程应用提供了基础。掌握这些基本概念后,你可以根据自己的需求组合这些组件以实现更高级的功能。以下是一些可能对你有帮助的高级用法:

线程池

线程池是一种允许您在一组线程中重用线程以执行任务的技术。这可以减少线程创建和销毁的开销,从而提高性能。C++11 没有提供内置的线程池功能,但您可以使用基本的多线程组件自己实现一个,或者使用第三方库(例如 Boost.Asio)。

并行算法

C++17 标准引入了并行算法库,它提供了一些与 STL 算法类似的并行版本,以支持多线程并行执行。这使得实现并行计算变得更加简单。例如,您可以使用 std::sort 的并行版本 std::execution::par大数据集进行排序

示例:

#include <iostream>#include <vector>#include <algorithm>#include <execution>#include <random>int main() {    std::vector<int> data(100000);    std::random_device rd;    std::mt19937 gen(rd());    std::generate(data.begin(), data.end(), [&]() { return gen() % 1000; });    std::sort(std::execution::par, data.begin(), data.end());    // 现在 data 已经被排序    return 0;}

lock_guard 和 scoped_lock:

std::lock_guard 是一个简化互斥锁管理的 RAII 封装。当您创建一个 lock_guard 对象时,它将自动锁定给定的互斥锁,并在销毁时自动解锁。这有助于避免死锁和忘记解锁。

std::scoped_lock 是 C++17 引入的一个改进版的 lock_guard,用于同时锁定多个互斥锁,避免死锁。

std::shared_mutex 和 std::shared_lock:

std::shared_mutex 是一种特殊类型的互斥锁,允许多个线程同时以共享模式访问资源。std::shared_lock 是与 std::shared_mutex 配合使用的锁对象,允许您在共享模式或独占模式下锁定资源。

以上是 C++11 多线程库的一些高级用法。熟练掌握这些组件和技巧可以帮助您实现更加高效、可扩展和健壮的多线程应用。

大体其实就这些,另外在设计时还需要注意的是:

避免死锁:

在多线程编程中,死锁是一个常见的问题,它发生在两个或多个线程相互等待对方释放资源时。为了避免死锁,请确保使用锁的顺序一致,避免嵌套锁,并尽量减少锁的使用范围。

数据竞争与内存模型:

在多线程环境中,数据竞争是一个潜在的问题。当多个线程同时访问共享数据且至少有一个线程对数据进行修改时,就会发生数据竞争。避免数据竞争的方法包括使用互斥锁、原子操作或者线程局部存储。

此外,C++11 引入了内存模型,用于描述多线程中的内存访问行为。内存模型包括原子操作的内存顺序,例如 std::memory_order_relaxedstd::memory_order_acquirestd::memory_order_release。在大多数情况下,默认的内存顺序已经足够使用,但在某些高级应用场景下,理解和使用内存模型可以帮助您实现更高效的代码。

性能与可伸缩性:

在编写多线程程序时,需要权衡性能和可伸缩性。线程之间的通信和同步会导致性能损失,因此您需要在使用更多线程以提高并发性能时,尽量减少同步和通信的开销。

异常安全:

在多线程环境中,处理异常尤为重要。在一个线程中发生异常时,其他线程可能仍在继续执行。确保在多线程中正确处理异常,例如使用 try-catch 块捕获异常,并确保锁和资源在异常发生时得到正确的释放。

第三方库和框架:

除了 C++ 标准库提供的多线程支持外,还有一些第三方库和框架提供了更高级或特定领域的多线程功能。例如,Boost.Thread 库提供了类似于 C++11 多线程库的功能,但在某些方面更为强大。Intel 的 Threading Building Blocks (TBB) 是另一个广泛使用的并行编程库。

P.S. 再来点与之无关紧要的小知识

join()既然是等待子线程完成,为什么不叫wait_thread()之类的? 而且join本身的单词是加入,有点感觉格格不入。

因为在多线程编程中,join 方法的命名来源于它的作用:将一个子线程加入(join)到主线程或其他线程,等待这个子线程完成。这种“加入”的概念实际上是指当前线程(通常是主线程)等待另一个线程(子线程)完成它的任务。在子线程完成任务之前,当前线程会阻塞等待。因此,join 这个名字来源于将子线程与等待它的线程连接在一起的过程。简单来说,当在疯狂星期四这天的公司楼下KFC里,在你排队买快乐时,有你的领导插队(join进来),你必须等它完成的,所以这也就是等待的本意。

wait()

std::condition_variable::wait() 是一个成员函数,用于阻塞当前线程,直到条件变量被通知。wait() 函数通常与 std::unique_lock<std::mutex>std::mutex 配合使用,以便在等待期间自动解锁互斥量。可以传递一个谓词函数给 wait(),以便在条件变量被通知后检查是否满足继续执行的条件。

notify_all()notify_one

std::condition_variable::notify_all() 是一个成员函数,用于唤醒所有等待当前条件变量的线程。当某个条件满足时,可以调用 notify_all() 通知所有等待的线程继续执行。这是一种线程之间协作的方式。

std::condition_variable::notify_one() 也是一个成员函数,用于唤醒一个正在等待该条件变量的线程。与之相对的,notify_all 是唤醒所有正在等待该条件变量的线程。在某些情况下,您可能只需要唤醒一个等待的线程,而不是所有线程,这时候就可以使用 notify_one

再聊聊第三方库(如:Boost.Thread)方式

#include <iostream>#include <boost/thread.hpp>void print_hello() {    std::cout << "Hello from thread!" << std::endl;}int main() {    boost::thread thread(print_hello);    thread.join();    std::cout << "Hello from main!" << std::endl;    return 0;}

最后的战役:操作系统方式

POSIX 线程(pthread):

POSIX 线程是基于 POSIX 标准的一种多线程实现,它在类 Unix 系统(如 linuxMacOS)中广泛使用。pthread 库提供了用于创建线程、同步、互斥锁等多线程功能的函数。然而,由于它是用 C 语言编写的,所以在 C++ 中使用时可能不够直观。以下是一个简单的使用 POSIX 线程的例子:

#include <iostream>#include <pthread.h>void* print_hello(void* arg) {    std::cout << "Hello from thread!" << std::endl;    return nullptr;}int main() {    pthread_t thread;    pthread_create(&thread, nullptr, print_hello, nullptr);    pthread_join(thread, nullptr);    std::cout << "Hello from main!" << std::endl;    return 0;}

Windows 线程:

在 Windows 操作系统中,可以通过 Windows api 来创建和管理线程。Windows API 提供了一组用于线程管理、同步和互斥的函数。以下是一个简单的使用 Windows 线程的例子:

#include <iostream>#include <windows.h>DWord WINAPI print_hello(LPVOID lpParam) {    std::cout << "Hello from thread!" << std::endl;    return 0;}int main() {    HANDLE thread = CreateThread(nullptr, 0, print_hello, nullptr, 0, nullptr);    WaitForSingleObject(thread, INFINITE);    CloseHandle(thread);    std::cout << "Hello from main!" << std::endl;    return 0;}

关于“C++中线程的原理与实现方法是什么”的内容就介绍到这里了,感谢大家的阅读。如果想了解更多行业相关的知识,可以关注编程网其他教程频道,小编每天都会为大家更新不同的知识点。

--结束END--

本文标题: C++中线程的原理与实现方法是什么

本文链接: https://lsjlt.com/news/354531.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

猜你喜欢
  • C++中线程的原理与实现方法是什么
    这篇文章主要介绍“C++中线程的原理与实现方法是什么”的相关知识,小编通过实际案例向大家展示操作过程,操作方法简单快捷,实用性强,希望这篇“C++中线程的原理与实现方法是什么”文章能帮助大家解决问题。在C++中有多种实现线程的方式C++11...
    99+
    2023-07-05
  • C/C++ 原生API实现线程池的方法是什么
    本篇内容主要讲解“C/C++ 原生API实现线程池的方法是什么”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“C/C++ 原生API实现线程池的方法是什么”吧!线程池有两个核心的概念,一个是任务队...
    99+
    2023-06-25
  • c++线程池实现的方法是什么
    C++线程池的实现方法可以使用C++中的多线程库,如std::thread和std::mutex等来实现。以下是一个简单的C++线程...
    99+
    2023-10-26
    c++
  • Java中实现线程池的原理是什么
    Java中实现线程池的原理是什么,相信很多没有经验的人对此束手无策,为此本文总结了问题出现的原因和解决方法,通过这篇文章希望你能解决这个问题。01.***制线程的缺点多线程的软件设计方法确实可以***限度地发挥多核处理器的计算能力,提高生产...
    99+
    2023-06-16
  • Java中线程池的实现原理是什么
    这篇文章给大家介绍Java中线程池的实现原理是什么,内容非常详细,感兴趣的小伙伴们可以参考借鉴,希望对大家能有所帮助。线程池是什么?我们可以利用java很容易创建一个新线程,同时操作系统创建一个线程也是一笔不小的开销。所以基于线程的复用,就...
    99+
    2023-05-31
    java 线程池 ava
  • JUC中wait与notify方法的实现原理是什么
    今天小编给大家分享一下JUC中wait与notify方法的实现原理是什么的相关知识点,内容详细,逻辑清晰,相信大部分人都还太了解这方面的知识,所以分享这篇文章给大家参考一下,希望大家阅读完这篇文章后有所收获,下面我们一起来了解一下吧。1.O...
    99+
    2023-07-05
  • C++中内存池的原理及实现方法是什么
    这篇文章主要讲解了“C++中内存池的原理及实现方法是什么”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“C++中内存池的原理及实现方法是什么”吧!为什么要用内存池C++程序默认的内存管理(ne...
    99+
    2023-07-05
  • C/C++ 原生API实现线程池的方法
    线程池有两个核心的概念,一个是任务队列,一个是工作线程队列。任务队列负责存放主线程需要处理的任务,工作线程队列其实是一个死循环,负责从任务队列中取出和运行任务,可以看成是一个生产者和...
    99+
    2024-04-02
  • Java线程池实现原理是什么
    这篇文章主要讲解了“Java线程池实现原理是什么”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“Java线程池实现原理是什么”吧!一、线程池参数corePoolSize(必填):核心线程数。m...
    99+
    2023-06-28
  • Java中ThreadLocal线程变量的实现原理是什么
    这篇文章主要介绍了Java中ThreadLocal线程变量的实现原理是什么的相关知识,内容详细易懂,操作简单快捷,具有一定借鉴价值,相信大家阅读完这篇Java中ThreadLocal线程变量的实现原理是什么文章都会有所收获,下面我们一起来看...
    99+
    2023-07-02
  • C++与Lua实现交互的原理是什么
    本篇文章给大家分享的是有关C++与Lua实现交互的原理是什么,小编觉得挺实用的,因此分享给大家学习,希望大家阅读完这篇文章后可以有所收获,话不多说,跟着小编一起来看看吧。具体步骤:1,找到cocos自带的绑定工具脚本文件genbinding...
    99+
    2023-06-06
  • C++位图的实现原理与方法
    概念 位图就是bitmap的缩写,所谓bitmap,就是用每一位来存放某种状态,适用于大规模数据,该数据都是不重复的简单数据。通常是用来判断某个数据存不存在的 例如:给40亿个不重...
    99+
    2024-04-02
  • Spring 中 load 方法的实现原理是什么?
    Spring 是一个流行的 Java 开发框架,它提供了很多方便的功能,其中之一是 load 方法。load 方法可以根据指定的类型和 ID 加载一个对象。在本文中,我们将深入探讨 Spring 中 load 方法的实现原理。 load ...
    99+
    2023-10-15
    load spring 编程算法
  • MD5算法原理及C#和JS实现的方法是什么
    本篇内容主要讲解“MD5算法原理及C#和JS实现的方法是什么”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“MD5算法原理及C#和JS实现的方法是什么”吧!一、简介MD5 是哈希算法(散列算法)的...
    99+
    2023-07-05
  • C++中继承的实现原理是什么
    C++中继承的实现原理是通过创建一个新的类(派生类)来继承已有的类(基类)的属性和方法。派生类可以访问基类中的非私有成员,并且可以扩...
    99+
    2024-02-29
    C++
  • Java/Go/Python/JS/C基数排序算法的原理与实现方法是什么
    这篇文章主要介绍“Java/Go/Python/JS/C基数排序算法的原理与实现方法是什么”的相关知识,小编通过实际案例向大家展示操作过程,操作方法简单快捷,实用性强,希望这篇“Java/Go/Python/JS/C基数排序算法的原理与实现...
    99+
    2023-07-05
  • web前端模板的原理与实现方法是什么
    这篇文章主要讲解了“web前端模板的原理与实现方法是什么”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“web前端模板的原理与实现方法是什么”吧!时下流行什么...
    99+
    2024-04-02
  • Java中Prime算法的原理是什么与怎么实现
    本篇内容主要讲解“Java中Prime算法的原理是什么与怎么实现”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“Java中Prime算法的原理是什么与怎么实现”吧!Prim算法介绍1.点睛在生成树...
    99+
    2023-07-02
  • C# 中get与post的原理是什么
    这期内容当中小编将会给大家带来有关C# 中get与post的原理是什么,文章内容丰富且以专业的角度为大家分析和叙述,阅读完这篇文章希望大家可以有所收获。C# get post中post和get的不同之处get与post的区别在于:(对于CG...
    99+
    2023-06-17
  • php多线程实现的方法是什么
    在PHP中实现多线程有以下几种方法:1. 使用PCNTL扩展:PCNTL扩展提供了一些函数,如`pcntl_fork()`和`pcn...
    99+
    2023-09-27
    php
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作