C++20 协程探索

c++20协程基础概念 c++协程 阅读详情

c++ 20 引入了协程的特性,很多其他语言很早就已经有了,c++ 的又会有什么不同呢,本文旨在了解协程的概念以及简单明白底层实现的原理。

Three new language keywords: co_await, co_yield and co_return

definition

当一个函数被阻塞时,一般会切换到其他线程去执行,而线程频繁切换是存在花销的。

如果不阻塞,而使用异步回调,会割裂了原来的代码业务逻辑,还会陷入回调地狱难以维护。

A coroutine is a generalisation of a function that allows the function to be suspended and then later resumed.

首先,一个协程就是一个函数,但它允许函数被挂起,之后再被恢复。

Coroutines are stackless: they suspend execution by returning to the caller and the data that is required to resume execution is stored separately from the stack.

线程可以在遇到阻塞的地方后,保存执行的上下文,转而去执行别处的代码。待阻塞的请求完成后,再转而回去继续执行。

线程是操作系统抽象出来的执行流,由操作系统统一调度管理。那在一个线程中,同样可以抽象出多个执行流,由线程来统一调度管理。这线程之上抽象的执行流就是协程。

线程的调度由操作系统来管理,是抢占式调度。而协程不同,协程需要互相配合,主动交出执行权,这也是协程的名字——协作式程序的来历。

normal call

A normal function can be thought of as having two operations: Call and Return (Note that I’m lumping “throwing an exception” here broadly under the Return operation). 一个普通函数可以简单地认为是两步操作:调用和返回。

The Call operation creates an activation frame, suspends execution of the calling function and transfers execution to the start of the function being called.

调用会创建函数栈,会把调用它的函数先挂起,然后执行被调用的函数。

The Return operation passes the return-value to the caller, destroys the activation frame and then resumes execution of the caller just after the point at which it called the function.

返回就是把返回值传给调用者,破坏函数栈,然后恢复为调用者执行。

coroutine

Coroutines generalise the operations of a function by separating out some of the steps performed in the Call and Return operations into three extra operations: Suspend, Resume and Destroy. 协程将调用和返回分成了三个额外的操作:挂起、恢复、销毁。

The Suspend operation suspends execution of the coroutine at the current point within the function and transfers execution back to the caller or resumer without destroying the activation frame. Any objects in-scope at the point of suspension remain alive after the coroutine execution is suspended.

挂起操作会挂起当前协程的执行,然后不会破坏函数栈,让调用者或恢复者来继续执行。协程挂起后,域内的对象还是保持存活。

Note that, like the Return operation of a function, a coroutine can only be suspended from within the coroutine itself at well-defined suspend-points.

注意,和返回操作一样,协程只能被自己挂起。

The Resume operation resumes execution of a suspended coroutine at the point at which it was suspended. This reactivates the coroutine’s activation frame.

恢复操作恢复挂起的协程继续执行,重新激活了它的函数栈。

The Destroy operation destroys the activation frame without resuming execution of the coroutine. Any objects that were in-scope at the suspend point will be destroyed. Memory used to store the activation frame is freed.

销毁操作破坏函数栈,域内对象也被销毁,不会把执行交给协程了。

Coroutine activation frames

Since coroutines can be suspended without destroying the activation frame, we can no longer guarantee that activation frame lifetimes will be strictly nested. This means that activation frames cannot in general be allocated using a stack data-structure and so may need to be stored on the heap instead.

协程可以被挂起而不破坏其函数栈,所以就不再保证其栈是紧凑的(每个函数调用栈挨着),说明其栈不能以一般方式分配,可能要放到堆内存存储了。

There are some provisions in the C++ Coroutines TS to allow the memory for the coroutine frame to be allocated from the activation frame of the caller if the compiler can prove that the lifetime of the coroutine is indeed strictly nested within the lifetime of the caller. This can avoid heap allocations in many cases provided you have a sufficiently smart compiler.

一些实现可以分配到栈上存储,但需要有个足够聪明的编译器。

With coroutines there are some parts of the activation frame that need to be preserved across coroutine suspension and there are some parts that only need to be kept around while the coroutine is executing. For example, the lifetime of a variable with a scope that does not span any coroutine suspend-points can potentially be stored on the stack.

协程挂起和协程执行时,只有一部分的栈数据是需要保留的。比如某些域内的变量不会越过挂起的点,可以被保存到栈内存上。

You can logically think of the activation frame of a coroutine as being comprised of two parts: the ‘coroutine frame’ and the ‘stack frame’.

所以可以认为协程的栈由两部分组成:协程的栈 和 函数栈。

The ‘coroutine frame’ holds part of the coroutine’s activation frame that persists while the coroutine is suspended and the ‘stack frame’ part only exists while the coroutine is executing and is freed when the coroutine suspends and transfers execution back to the caller/resumer.

协程栈保留挂起需要用到的数据,而函数栈用以协程执行,协程挂起后函数栈就可以释放掉了。

Suspend

In the C++ Coroutines TS, these suspend-points are identified by usages of the co_await or co_yield keywords.

When a coroutine hits one of these suspend-points it first prepares the coroutine for resumption by:

如果触发挂起点,协程需要准备好恢复所需要用到的东西

  • Ensuring any values held in registers are written to the coroutine frame

保证存在寄存器的变量写到协程栈中

  • Writing a value to the coroutine frame that indicates which suspend-point the coroutine is being suspended at. This allows a subsequent Resume operation to know where to resume execution of the coroutine or so a subsequent Destroy to know what values were in-scope and need to be destroyed.

写入一个值到协程栈中,来表示协程是在哪个挂起点被挂起的。让恢复可以正确执行,或者让销毁可以正确地释放内存。

Resume

Just like a normal function call, this call to resume() will allocate a new stack-frame and store the return-address of the caller in the stack-frame before transferring execution to the function.

跟普通函数调用一样,会先创建函数栈保存调用者的返回地址。

However, instead of transferring execution to the start of the function it will transfer execution to the point in the function at which it was last suspended. It does this by loading the resume-point from the coroutine-frame and jumping to that point.

它会从协程栈加载挂起点信息,然后跳过去。

Destroy

void destroy()

The Destroy operation destroys the coroutine frame without resuming execution of the coroutine. 直接销毁协程栈

This operation can only be performed on a suspended coroutine. 只能用于被挂起的协程

The Destroy operation acts much like the Resume operation in that it re-activates the coroutine’s activation frame, including allocating a new stack-frame and storing the return-address of the caller of the Destroy operation.

像恢复操作一样,销毁操作会分配新的函数栈保存调用者的返回地址。

However, instead of transferring execution to the coroutine body at the last suspend-point it instead transfers execution to an alternative code-path that calls the destructors of all local variables in-scope at the suspend-point before then freeing the memory used by the coroutine frame.

但它会跳转到额外的代码去执行销毁操作。

Call

The Call operation of a coroutine is much the same as the call operation of a normal function. In fact, from the perspective of the caller there is no difference.

协程里的调用操作与普通函数调用类似。

However, rather than execution only returning to the caller when the function has run to completion, with a coroutine the call operation will instead resume execution of the caller when the coroutine reaches its first suspend-point.

但是,普通函数要运行完才能返回到调用者,协程可以在挂起点返回给调用者。

When performing the Call operation on a coroutine, the caller allocates a new stack-frame, writes the parameters to the stack-frame, writes the return-address to the stack-frame and transfers execution to the coroutine. This is exactly the same as calling a normal function.

当在协程里使用调用操作,调用者会分配函数栈写入参数和返回地址,然后跳转到被调用的函数。

The first thing the coroutine does is then allocate a coroutine-frame on the heap and copy/move the parameters from the stack-frame into the coroutine-frame so that the lifetime of the parameters extends beyond the first suspend-point.

不过协程之后会分配协程栈,把函数栈上的参数复制或者移动到协程栈,从而使这些参数的生命周期得以在挂起点延长。

Return

The Return operation of a coroutine is a little different from that of a normal function.

返回操作和普通函数不太一样。

When a coroutine executes a return-statement (co_return according to the TS) operation it stores the return-value somewhere (exactly where this is stored can be customised by the coroutine) and then destructs any in-scope local variables (but not parameters).

当协程执行返回操作,它会保存返回值到某处,然后销毁域内的局部变量。

The coroutine then has the opportunity to execute some additional logic before transferring execution back to the caller/resumer.

在返回到调用者和恢复者之前,协程还可以做些额外的逻辑。

This additional logic might perform some operation to publish the return value, or it might resume another coroutine that was waiting for the result. It’s completely customisable.

可能是输出返回值,也可能是恢复在等待返回的另一个协程。

The coroutine then performs either a Suspend operation (keeping the coroutine-frame alive) or a Destroy operation (destroying the coroutine-frame).

然后协程会执行挂起或销毁操作。

Execution is then transferred back to the caller/resumer as per the Suspend/Destroy operation semantics, popping the stack-frame component of the activation-frame off the stack.

然后返回给调用者或恢复者执行,退栈函数栈。

It is important to note that the return-value passed to the Return operation is not the same as the return-value returned from a Call operation as the return operation may be executed long after the caller resumed from the initial Call operation.

值得注意的是,返回值可能在调用者从初始调用恢复之后很久才会被执行。

2、An illustration

let’s say we have a function (or coroutine), f() that calls a coroutine, x(int a).

举个例子,普通函数 f 调用协程 x。

Before the call we have a situation that looks a bit like this: 调用前长这样

Then when x(42) is called, it first creates a stack frame for x(), as with normal functions.

调用协程 x,为 x 创建函数栈

Then, once the coroutine x() has allocated memory for the coroutine frame on the heap and copied/moved parameter values into the coroutine frame we’ll end up with something that looks like the next diagram. Note that the compiler will typically hold the address of the coroutine frame in a separate register to the stack pointer (eg. MSVC stores this in the rbp register).

然后,协程 x 在堆内存中分配协程栈,把参数复制或移动到协程栈。而当前 rbp 会指向协程栈。

If the coroutine x() then calls another normal function g() it will look something like this.

如果协程 x 调用了另一个普通函数 g

When g() returns it will destroy its activation frame and restore x()’s activation frame. Let’s say we save g()’s return value in a local variable b which is stored in the coroutine frame.

当普通函数 g 返回,它会销毁函数栈,恢复 x 的函数栈,把返回值存储到协程栈当中。

If x() now hits a suspend-point and suspends execution without destroying its activation frame then execution returns to f().

如果协程 x 执行到挂起点,不会销毁协程栈,执行返回到 f

This results in the stack-frame part of x() being popped off the stack while leaving the coroutine-frame on the heap. When the coroutine suspends for the first time, a return-value is returned to the caller. This return value often holds a handle to the coroutine-frame that suspended that can be used to later resume it. When x() suspends it also stores the address of the resumption-point of x() in the coroutine frame (call it RP for resume-point).

函数栈中,x 会被退栈,而 rbp 也会离开协程栈。当协程第一次挂起,会返回一个值给调用者,这个值会有处理协程栈的挂起与恢复的控制器。当协程 x 挂起,会在协程栈中保存挂起点信息。

This handle may now be passed around as a normal value between functions. At some point later, potentially from a different call-stack or even on a different thread, something (say, h()) will decide to resume execution of that coroutine. For example, when an async I/O operation completes.

控制器可以被函数间传递。之后的某个地方,可能在不同的调用栈,或者不同的线程,可能函数 h 决定恢复协程。

The function that resumes the coroutine calls a void resume(handle) function to resume execution of the coroutine. To the caller, this looks just like any other normal call to a void-returning function with a single argument.

函数会调用 resume(handle) 来恢复协程,在调用者看来就是普通的单个参数的函数返回。

This creates a new stack-frame that records the return-address of the caller to resume(), activates the coroutine-frame by loading its address into a register and resumes execution of x() at the resume-point stored in the coroutine-frame.

这会创建新的函数栈保存调用者的返回地址,激活协程栈,把它的地址加载到寄存器中,恢复协程在挂起点的执行。

编写

interface

An interface describes the behavior or capabilities of a C++ class without committing to a particular implementation of that class.

The C++ interfaces are implemented using abstract classes and these abstract classes should not be confused with data abstraction which is a concept of keeping implementation details separate from associated data.

example

#include <concepts>
#include <coroutine>
#include <exception>
#include <iostream>

// the return type of a coroutine—call it R—must be an object type with a nested type R::promise_type.
struct ReturnObject {
  struct promise_type {
// R::promise_type must include a method R get_return_object() that returns an instance of the outer type R. 
// The result of get_return_object() is the return value of the coroutine function
    ReturnObject get_return_object() { return {}; }
    std::suspend_never initial_suspend() { return {}; }
    std::suspend_never final_suspend() { return {}; }
    void unhandled_exception() {}
  };
};

struct Awaitable {
  std::coroutine_handle<> *hp_;
// await_ready is an optimization. 
// If it returns true, then co_await does not suspend the function. 
// Of course, you could achieve the same effect in await_suspend, 
// by resuming (or not suspending) the current coroutine, 
// but before calling await_suspend, 
// the compiler must bundle all state into the heap object referenced by the coroutine handle, 
// which is potentially expensive.
  constexpr bool await_ready() const noexcept { return false; }
// we store the coroutine handle every time await_suspend is called, 
// but the handle does not change across invocations. 
// (Recall the handle is like a pointer to the coroutine state, 
// so while value of i may change in this state, the pointer itself remains the same.)
///  void await_suspend(std::coroutine_handle<> h) { *hp_ = h; }
    void Awaiter::await_suspend(std::coroutine_handle<> h) {
      if (hp_) { // 只设置一次就可以了
        *hp_ = h; // 这里是将 coroutine handle 放入 main1 的里面
        hp_ = nullptr;
      }
    }
// await_resume here returns void, but if instead it returned a value, 
// this value would be the value of the co_await expression
  constexpr void await_resume() const noexcept {}
};

ReturnObject counter(std::coroutine_handle<> *continuation_out) {
  Awaitable a{continuation_out}; // coroutine defined
// Creates a callable object that, when invoked, 
// will resume execution of the coroutine at the point 
// immediately following evaluation of the co_await expression
  for (unsigned i = 0;; ++i) {
// When you evaluate the expression co_await a, 
// the compiler creates a coroutine handle 
// and passes it to the method a.await_suspend(coroutine_handle).
    co_await a; // resume point
    std::cout << "counter: " << i << std::endl;
  }
}

void main1() {
  std::coroutine_handle<> h; // callable object
  counter(&h);
  for (int i = 0; i < 3; ++i) {
    std::cout << "In main1 function\n";
    h();
  }
// It can be easily copied, but it doesn’t have a destructor 
// to free the memory associated with coroutine state. 
// To avoid leaking memory, you must generally destroy coroutine state
  h.destroy();
}

/*
    Output:
    In main1 function
    counter: 0
    In main1 function
    counter: 1
    In main1 function
    counter: 2
*/

变种

Instead of passing a coroutine_handle<>* into counter, it would be nicer if we could just return the handle from counter(). We can do that if we put the coroutine handle inside the return object.

struct ReturnObject2 {
  struct promise_type {
    ReturnObject2 get_return_object() {
      return {
        // Uses C++20 designated initializer syntax
        .h_ = std::coroutine_handle<promise_type>::from_promise(*this)
      };
    }
    std::suspend_never initial_suspend() { return {}; }
    std::suspend_never final_suspend() { return {}; }
    void unhandled_exception() {}
  };

  std::coroutine_handle<promise_type> h_;
  operator std::coroutine_handle<promise_type>() const { return h_; }
};

ReturnObject2 counter2() {
  for (unsigned i = 0;; ++i) {
    co_await std::suspend_always{};
    std::cout << "counter2: " << i << std::endl;
  }
}

void main2() {
  // A coroutine_handle<promise_type> converts to coroutine_handle<>
  std::coroutine_handle<> h = counter2();
  for (int i = 0; i < 3; ++i) {
    std::cout << "In main2 function\n";
    h();
  }
  h.destroy();
}

参考&推荐阅读

Coroutine Theory:https://lewissbaker.github.io/2017/09/25/coroutine-theory

C++ Coroutines: Understanding operator co_await:https://lewissbaker.github.io/2017/11/17/understanding-operator-co-await

C++ Coroutines: Understanding the promise type:https://lewissbaker.github.io/2018/09/05/understanding-the-promise-type

C++ Coroutines: Understanding Symmetric Transfer:https://lewissbaker.github.io/2020/05/11/understanding_symmetric_transfer

Coroutines (C++20):https://en.cppreference.com/w/cpp/language/coroutines

神秘使者到Java帝国传道协程,竟被轰了出去:https://mp.weixin.qq.com/s/cN9dC_crrEU579-AMrd5PQ

https://www.scs.stanford.edu/~dm/bl

C++20中的协程(Coroutine) 目录C++20中的协程(Coroutine)那么,什么是协程?那么这么好用的协程,是不是只要C++20一推出,我们加上一个关键字就能直接把异步调用转化为同步调用呢?协程函数和Awaitable类那么,到底要如何使用协程呢?写在最后 C++20中的协程(Coroutine) 从2017年开始, 协程(Coroutine)的概念就开始被建议加入C++20的标准中了,并已经开始有人对C++20协程的提案进行了介绍。1事实上,协程的概念在很早就出现了,甚至其他语言(JS,Python,C#等)早就已经支持了协程。 阅读详情

相关推荐

C++20中的协程(Coroutine)功能解析:co_await

C++20 协程的co_await实现

fangfanglovezhou的博客 4147

C/C++实现协程及原理(详细完整版)-架构师篇

一、协程(Coroutine)简介 协程,又称微线程,纤程。英文名Coroutine。 协程的概念很早就提出来了,但直到最近几年才在某些语言(如Lua)中得到广泛应用。 子程序,或者称为函数,在所有语言中都是层级调用,比如A调用B,B在执行过程中又调用了C,C执行完毕返回,B执行完毕返回,最后是A执行完毕。 所以子程序调用是通过栈实现的,一个线程就是执行一个子程序。 子程序调用总是一个入口,一次返回,调用顺序是明确的。而协程的调用和子程序不同。 协程看上去也是子程序,但执行过程中,在子程序内部可中断,然后转

嵌入式Linux内核的博客 1万+

59、探索C++20的核心特性:概念、范围、协程和模块

本文深入探讨了C++20的四大核心特性:概念、范围、协程和模块,并进一步介绍了其他重要特性,如自动比较运算符、日历和时区库、格式库等。通过实际代码示例,展示了这些特性如何帮助开发者编写更简洁、高效和可靠的现代C++代码。

rgv23456789的博客 436

C++,什么是协程?

协程是一种特殊的函数,可以在执行过程中暂停并在稍后恢复执行。与普通函数不同,协程可以在任意点挂起(yield)并将控制权返回给调用者,同时保留其状态,以便后续恢复执行。协程常用于异步编程、生成器、状态机等场景。 C++20 引入了对协程的原生支持,使得开发者可以更方便地编写协程代码。

汽车电子软件领域知识分享 3013

38、探索C++20的新特性:概念、模块、范围库和协程

本文深入探讨了C++20的新特性,包括概念(Concepts)、模块(Modules)、范围库(Ranges Library)和协程(Coroutines)等核心功能。同时介绍了标准库的增强部分,如`std::span`、日历和时区支持,并分享了性能优化技巧和最佳实践,帮助开发者更好地利用C++20编写高效且易于维护的代码。

rgv23456789的博客 298

30、探索C++20中的协程与线程安全机制

本文深入探讨了C++20中的协程和线程安全机制,通过实例和代码片段展示了如何在实际项目中应用这些特性。内容涵盖了协程的基础与高级用法、线程安全机制(如原子操作、同步输出流、智能指针)以及并发编程的最佳实践。帮助读者理解并掌握C++20的新特性,编写更高效、健壮的并发程序。

rgv23456789的博客 528

探索 Kelcoro:C++20 协程库的新星

探索 Kelcoro:C++20 协程库的新星 在现代C++编程领域,协程(coroutines)已成为处理异步操作和并发编程的强大工具。今天,我们将介绍一个令人兴奋的开源项目——Kelcoro,这是一个基于C++20标准的协程库,支持clang、gcc和msvc编译器。本文将深入探讨Kelcoro的项目介绍、技术分析、应用场景及其独特特点,帮助你了解为何Kelcoro值得你的关注和使用。 项目介...

gitblog_00680的博客 436

C++20 协程的探索与实践

本文深入探讨了C++20中引入的协程技术,通过实例代码和详细分析展示了协程在多线程环境下的强大功能和易用性。文章首先介绍了协程的基本概念,然后通过案例分析了协程在实际开发中的应用,包括线程同步、生成器模式以及性能测试。通过实践,我们能够更好地理解协程的工作原理,并且感受到在C++23中std::generator的引入进一步增强了协程的可用性。

weixin_42465140的博客 334

C++ 20 协程的探索

编译器扫描到了协程函数,会定义一个协程管理对象来管理协程协程管理对象被创建之后,执行此时外界有机会获取到管理对象,此时我们将handle 对象保存到了 Generator 对象中协程管理对象执行函数,对象根据函数的返回值来决定是否要执行协程函数。这里我们通过返回来将协程挂起外界通过执行move_next调用里面的h.resume()来恢复协程的执行,协程会按顺序执行直到遇到 co_yield、co_await、co_return这里遇到了关键字co_yield。

Masimaro的专栏 410

C++20协程的探索与实践

本章深入探讨了C++20中协程的实现与应用,包括协程的基本结构、生成器的创建、以及如何利用协程进行线程同步和异步编程。通过具体的代码示例和详细的解释,揭示了协程在现代编程中的强大功能。

weixin_31776191的博客 327

C++20协程与并行算法的探索

本文深入探讨了C++20新引入的协程概念及其在并行算法中的应用。通过HPX库的实例,展示了如何利用协程提高代码的并行性能。文中还讨论了并行算法对性能的影响,并对不同并行策略进行了性能研究。

weixin_31869917的博客 417

kelcoro: C++20协程库的探索与应用

kelcoro: C++20协程库的探索与应用 项目介绍 kelcoro 是一个基于C++20的协程库,由Kelbon开发并维护。它提供了一套高效且现代的工具集,旨在简化C++中异步编程的复杂性,使得开发者能够利用协程特性轻松实现非阻塞式流程控制。Apache-2.0许可协议下开源,截至最新统计,已收获117颗星,8个fork,表明社区对其有一定的认可与兴趣。 项目快速启动 要快速启动使用 ke...

gitblog_00369的博客 856

wsl + vscode 离线配置 ERROR: Faild to download https://update.code.visualstudio.com

很多情况下是没有现成的VS Code Server程序的,需要手动下载。记住上面的Commit ID,可以通过下面的链接下载 vscode-server-linux-x64.tar.gz。然后将vscode-server-linux-x64.tar.gz上传在服务器上的$HOME/.vscode-server/bin文件夹中,解压。在wsl中下载不了 vscode 的核心。运行下面两行命令,建立空的$HOME/.vscode-server/bin文件夹。重新运行 wsl与vs的链接即可。

点云算法工程师 3789

【C++高级开发应用篇】探索C++20中的协程:异步编程的强大工具

【C++高级开发应用篇】探索C++20中的协程:异步编程的强大工具

编程技术探索者,分享C/C++、C#、Java、数据库等开发经验,聚焦实战技巧与AI兴趣,助力编程爱好者成长。 2467

python数据处理与分析案例,python如何进行数据处理

大家好,本文将围绕基于python的数据处理案例展开说明,python数据处理与分析案例是一个很多人都想弄明白的事情,想搞清楚python如何进行数据处理需要先了解以下几个事情。大家好,小编来为大家解答以下问题,python数据处理案例步骤及其代码,python数据处理与分析教程,今天让我们一起来看看吧!哈喽~今天学习记录的是数据分析实训小案例。就用这个案例来好好巩固一下 python 数据分析三剑客。

a17348的博客 1237

UE4 Instanced Mesh 半透明渲染顺序不对解决

以绿色方块为Instanced Mesh为例。 正确的半透应该是这样的: 这是一般蓝图Actor方块,其半透是正常的。 然而Instanced Mesh因为过多,UE4不给你缓存正确的渲染顺序。所以,在不改代码的情况下,想解决Instanced Mesh半透问题,是不可能的。不正确的半透如下: 注意到错误发生在Instanced Mesh的方块与方块之间。 但我就是要解决,...

qq_41524721的博客 5569

探索C++20中的协程从基础实现到实际应用

C++20协程为异步编程提供了强大的语言级支持,使得编写高效、可维护的异步代码成为可能。从简单的生成器到复杂的异步系统,协程展现了其多样化的应用潜力。尽管协程的概念和实现机制有一定复杂性,但一旦掌握,它们将成为C++开发者工具箱中的宝贵资产。随着编译器对C++20支持的不断完善,以及社区经验的积累,我们可以预期协程将在未来的C++项目中扮演越来越重要的角色。理解并熟练运用这一特性,将有助于开发出更高效、更易维护的C++应用程序。

kangzhijia的专栏 748

汽车ECU诊断入门:手把手教你用UDS协议(ISO14229)读取和清除故障码

本文详细介绍了如何使用UDS协议(ISO14229)进行汽车ECU诊断,包括故障码读取与清除的完整流程。从诊断环境搭建、UDS协议基础到实战案例分析,手把手教你掌握0x19诊断服务、安全访问机制等核心技能,适用于汽车电子工程师和诊断开发人员。

weixin_33701251的博客 451

LibreSprite v1.2 更新解读:能跑进浏览器、支持压感笔,还能把像素画变成 3D 模型

LibreSprite v1.2 是这款开源像素画工具的又一次版本迭代。它带来三件实际有用的事:像素画工具可以直接在浏览器里打开使用;压感笔在绘图板上真正可用;内置示例脚本能把 2D 精灵渲染成立方体 3D 对象,并导出 STL 文件送去 3D 打印。 [![LibreSprite v1.2 像素画工具主界面,左侧调色板、右侧工具栏与动画预览窗口](https://raw.gitcode.com

gitblog_00582的博客 380

LVGL 拨盘菜单(Dial Menu)

h。

摸鱼专家 191

基于Simulink的鲸鱼优化算法(WOA)MPPT仿真

摘要:本文介绍了基于Simulink的鲸鱼优化算法(WOA)在光伏MPPT控制中的应用。WOA模拟座头鲸的"气泡网捕食策略",通过包围猎物、螺旋收缩和随机搜索三阶段动态平衡全局探索与局部开发能力,有效解决局部阴影导致的多峰P-V曲线问题。文章详细讲解了WOA-MPPT的Simulink建模过程,包括多峰光伏阵列建模、WOA控制器实现和Boost变换器控制。仿真结果显示,在三种典型工况下,WOA-MPPT的追踪效率>99.3%,动态响应时间<0.15s,性能优于传统算法。最后提出了混合WO

amy_mhd的博客 71

探索高效并发:Asyncio C++20协程库

在现代软件开发中,高效的并发处理是提升应用性能的关键。今天,我们将介绍一个令人兴奋的开源项目——Asyncio,这是一个基于C++20的协程库,旨在通过模仿Python的asyncio库,让C++开发者也能享受到简洁而强大的异步编程体验。 ## 项目介绍 Asyncio是一个C++20协程库,它允许开发者使用`await`语法编写并发代码。这个库不仅模仿了Python的asyncio库,还提供...

gitblog_00973的博客 642

Sprd Android 13 备忘 OTA升级app没更新到目标版本,恢复出厂才生效修改这个值

【代码】Sprd Android 13 备忘 OTA升级app没更新到目标版本,恢复出厂才生效修改这个值。

m0_60898338的博客 679

探索C++20中的协程从理论到实践的全面解析

awaitable控制协程的暂停和恢复逻辑;C++20引入的协程是无栈协程(stackless coroutines),这意味着它们不拥有独立的调用栈,而是借助编译器的转换来实现状态的保存和恢复。例如,在网络编程中,可以使用协程以同步方式编写异步网络请求代码,大幅提高代码的可读性和可维护性。随着C++20标准的正式发布,协程(Coroutines)作为其中最引人注目的特性之一,为C++异步编程带来了革命性的变化。同时,开发者社区也在积极探索协程在各种领域的创新应用,这将进一步推动C++异步编程范式的发展。

chdliudong的博客 423

Linux系统安装部署Redis完整教程(图文详解)

最近需要在Linux服务器安装部署Redis,参考了网上很多的文章,比较零碎,所以我自己写了一篇完整的Redis安装部署文档,在部署Redis之前重装了我的华为服务器,就是为了给大家一步步阐述我的操作过程,配合图文进行讲解更加通俗易懂!

黄团团的个人博客 6690

WINCC7.3破解文件

WINCC7.3硬件够破解文件,有需要的下载!WINCC7.3硬件够破解文件,有需要的下载!

驾驭C++20协程从基础实践到异步编程模式探索

要创建自定义协程类型,需要定义相应的承诺类型。承诺类型负责控制协程的行为,包括初始挂起、最终挂起、未处理异常以及返回值处理。通过特化std::coroutine_traits,可以将自定义类型与协程关联起来。一个典型的承诺类型需要定义以下方法:get_return_object()用于创建返回给调用者的对象;initial_suspend()和final_suspend()控制协程开始和结束时的挂起行为;return_void()或return_value()处理返回值;

lhblg2012的博客 492

NTscan扫描工具

ntscan是一个扫描器,可以扫描没有密码和弱密码的共享,先到配置文件中填写猜测的用户名和密码,扫描过程中右侧出现的是扫描日志。

上一篇: lld 链接器简述
下一篇: 《go 语言圣经》笔记
流左沙
博客等级 码龄11年 46粉丝 55原创
评论 6
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值