5.1k words
什么是协程我们在学习编程的过程中,逐渐从单线程,到多线程,再到异步编程和并发处理 这些异步与并发的任务不断增加,导致回调结构会变得复杂,为了提高代码的可读性和可维护性,协程(Coroutine)就被引入了 用来简化整个过程的操作 协程是一种特殊的函数,可以在中途暂停,并在稍后恢复执行 与线程不同,协程不会占用独立的系统资源,而是可以由程序直接控制何时暂停和恢复 因此协程其实就是属于某个具体的线程的,可以理解为一个单独的函数,一个单独的执行流,好处就是没有线程的上下文切换的消耗,协程的调度也是自行实现的,会更加灵活 为什么需要协程简单理解的话,可以理解为,虽然线程已经能够完成绝大部分的任务了,但是线程的粒度还不够精细 举个例子 在网络中,我们调用read函数读取数据,如果缓冲区没有数据的话,整个线程就会一直阻塞到缓冲区有数据,虽然我们可以采用IO多路复用来避免这个问题,但是这也是一种消耗,而且写起来实在不合直觉 如果能把线程细分为更多的子任务就好了,我们可以主动控制多个子任务进行交替执行 假设read函数是一个单独的子任务,当read函数发现缓冲区没有内容时,则直接“阻塞”,让...
2k words
在 Linux 和 Unix 系统中,文件描述符(File Descriptor, FD) 是系统管理打开文件和资源的一种抽象概念。文件描述符不仅用于文件,还用于网络套接字、管道等。理解文件描述符及其管理方式(如 select、poll 和 epoll)是进行系统编程的基础。 一、文件描述符是什么?文件描述符是操作系统中分配给每个打开文件或资源的一个非负整数。系统会为进程分配一张文件描述符表(FD Table),其中每个打开的文件或资源都会占用一个文件描述符,进程通过文件描述符来读写、控制和管理这些资源。 每个进程在打开文件、套接字等资源时,操作系统会分配一个可用的文件描述符。一般情况下,标准输入、标准输出和标准错误的文件描述符分别为 0、1 和 2。例如: 标准输入:文件描述符为 0 标准输出:文件描述符为 1 标准错误:文件描述符为 2 二、文件描述符的基本操作在 C 和 C++ 等系统编程语言中,文件描述符可通过系统调用(如 open、read、write、close 等)进行操作。以下是常用的文件描述符相关操作: open:打开文件,返回文件描述符。 1int f...
1.4k words
在 Linux 下进行多线程编程时,线程同步是至关重要的部分,尤其是在多个线程需要共享资源的场景中。尽管同步机制能够解决竞争条件,但它也带来了死锁的风险。死锁是指多个线程互相等待对方释放锁而导致永远无法继续执行的现象。为了避免死锁,可以从破坏导致死锁的四个必要条件入手。本文将从这四个条件展开,探讨如何通过破坏这些条件来避免死锁。 死锁的四个必要条件根据操作系统中的经典理论,死锁的产生必须满足以下四个必要条件: 互斥条件:某些资源是只能被一个线程独占使用的。 持有并等待条件:一个线程已经持有了某个资源,同时它在等待获取其他线程持有的资源。 不可剥夺条件:已经获得的资源不能被强行剥夺,线程只能主动释放资源。 循环等待条件:存在一个线程循环等待的链,链中的每个线程都在等待下一个线程持有的资源。 为了避免死锁,可以通过破坏至少其中一个条件来打破死锁局面。下面,我们将详细探讨这四个条件以及如何在 Linux 下的线程同步机制中破坏这些条件来避免死锁。 破坏死锁条件的方法破坏互斥条件互斥条件是指某些资源只能被一个线程独占使用,无法同时被多个线程访问。在某些情况下,我们可以通过将资源转...
1.2k words
在现代应用程序开发中,性能和并发性是许多项目的关键因素。特别是在使用 C++ 进行开发时,多线程编程已经成为提升性能的主要手段之一。然而,多线程编程带来了诸如线程同步、数据竞争等问题,调试起来难度较大。此外,C++ 手动管理内存,内存泄漏问题也频繁出现。幸运的是,Linux 提供了多种调试工具来帮助我们排查多线程相关的问题和内存泄漏。本文将介绍一些常见的 Linux 下用于 C++ 的多线程调试和内存泄漏检测工具。 一、多线程调试工具1. GDB:多线程调试的利器GDB 是 Linux 下最常用的调试器,支持调试多线程应用程序。对于多线程程序,GDB 自动检测并管理线程信息,允许我们查看每个线程的状态、切换线程、设置断点等。 基本使用方法: 启动 GDB 调试: 1gdb ./your_program 常用的 GDB 命令: info threads:列出当前程序中的所有线程及其状态。 thread <thread_id>:切换到指定线程,方便对特定线程进行调试。 thread apply all <command>:对所有线程执行指定命令,例如查看...
C++
1.1k words
在并发编程中,多个线程共享资源时,互斥同步是避免数据竞争和保证数据一致性的关键手段。C++11引入了一系列强大的线程和同步库,包括互斥(mutex)和条件变量(condition_variable) 本篇博客将详细介绍 C++11 中的互斥同步机制,重点包括 std::mutex、std::lock_guard、std::unique_lock 以及条件变量的使用场景和实现原理。 互斥锁:std::mutex什么是 std::mutexstd::mutex 是 C++11 提供的最基本的互斥锁,用于保护共享资源,确保同一时刻只有一个线程能够访问该资源。它提供了 lock() 和 unlock() 两个基本方法,分别用于加锁和解锁。 std::mutex 的使用1234567891011121314151617181920212223#include <iostream>#include <thread>#include <mutex>std::mutex mtx; // 全局互斥锁int counter = 0; // 全局共享资源void ...
C++
1.5k words
在 C++ 语言中,内存管理一直是开发者关注的重点。相较于其他语言,C++ 允许开发者直接管理内存和资源(如文件、网络连接等),虽然灵活,但也容易导致内存泄漏、资源泄露等问题。为了减少这些问题,C++ 提供了一种强大的设计模式:RAII(Resource Acquisition Is Initialization),即资源获取即初始化。RAII 为 C++ 的内存管理提供了一个结构化、可靠的解决方案,使程序员能够高效、安全地管理资源。本文将深入介绍 RAII 概念及其在内存管理中的重要性。 一、RAII 的基本概念RAII 是 C++ 的一种编程惯用法,其核心思想是:将资源的获取和释放与对象的生命周期绑定。即,资源的分配在对象构造时完成,资源的释放则在对象析构时自动进行。这样可以确保资源总是能在不需要时正确释放,从而避免资源泄漏。 1. RAII 的实现步骤 构造函数负责资源获取:在构造函数中获取所需的资源(如分配内存、打开文件等)。 析构函数负责资源释放:当对象超出作用域时,析构函数会自动调用,释放相关资源。 2. RAII 的两个关键点 自动化资源管理:通过对象生命周期管...
868 words
在 Linux 和其他 Unix-like 操作系统中,exec 系列函数和 fork 函数是进程管理中的两个重要组成部分。它们在创建和执行进程时扮演着关键角色,理解它们的工作原理及相互关系对于系统编程至关重要。 一、fork() 函数fork() 是用于创建新进程的系统调用。调用 fork() 后,当前进程(父进程)会被复制一份,生成一个新的进程(子进程)。以下是 fork() 的一些重要特性: 进程复制:子进程是父进程的一个副本,拥有相同的内存空间、打开的文件描述符等。 返回值:fork() 在父进程中返回新创建的子进程的 PID(进程 ID),而在子进程中返回 0。在出错时返回 -1。 并行执行:父进程和子进程会并行执行,操作系统调度它们的执行。 示例代码:123456789101112131415161718#include <stdio.h>#include <unistd.h>int main() { pid_t pid = fork(); if (pid < 0) { perro...
C++
621 words
C++的智能指针是RAII的一种应用 自动管理动态内存的工具 可以避免显示的内存释放 减少内存泄漏和为定义行为的可能性 C++标准库提供了三种主要的智能指针 unique_ptr shared_ptr weak_ptr 可能有的同学听过auto_ptr 这个东西在C++11被弃用 在C++17被移除 主要是由于复制问题 可能会导致内存重复释放 unique_ptr这个智能指针对所指对象是独占所有权的 也就是说 同一时刻 只有一个unique_ptr指向这个对象 他不允许复制 但是可以使用move变为右值 再移动赋值 将这个对象的所有权转移到另一个unique_ptr 这个智能指针只需要管理一个对象 开销也比较小 适用于高性能的场景 比如说文件具柄 数据库连接等 12345678910111213141516171819#include <memory>std::unique_ptr<int> createUniquePtr() { return std::make_unique<int>(10);}int main(...