首页 > 编程语言 >21. C++快速入门--协程 Coroutine 入门

21. C++快速入门--协程 Coroutine 入门

时间:2024-12-30 18:20:41浏览次数:1  
标签:co 入门 await return promise 协程 type 21

参考:
https://www.cnblogs.com/blizzard8204/p/17563217.html
https://www.bennyhuo.com/2022/03/09/cpp-coroutines-01-intro/
本文不完整, 更新中

1 基本概念

什么是协程?
  • C++ 20 的协程是一个特殊函数, 具有挂起和恢复的能力. (可以不一次性执行)
  • 协程可用于异步编程, 提供了一种更轻量级的并发机制
  • 使用了 co_awaitco_yieldco_return 的函数都是协程
  • 协程的返回类型必须满足条件: [[#协程的返回类型]]
c++20协程的缺陷
当前协程的实现非常复杂, 目前只提供了一些语法糖之类的东西, 需要根据很多规则编写代码, 最后由编译器依靠这些代码生成更复杂的代码. 就像 QT 框架那样
因此当前协程的范式是不完善的
协程是如何实现的

协程会在开始执行时的第一步就使用 operator new 来开辟一块内存来存放信息
包含如下内容

  • 协程传入的参数
  • 返回的 promise_type 类型对象 (也就是说, 返回对象一开始就创建了)
  • 协程的其他状态 (比如挂起点)

因此当需要暂停时, 那些状态被保存着, 等待恢复时使用.
协程执行完成 或 被销毁之后,协程的状态也被销毁释放

无栈协程和有栈协程
有栈:

  • 为每一个协程创建一个独立的内存栈进行上下文的保存和函数调用
  • boost 实现了有栈协程
    无栈:
  • 一开始就会在上保存所有的协程函数的“临时变量”以及调用参数等上下文信息
  • 从协程函数里切换出来的时候,因为大多数东西都是保存在上的,所以切换动作可以很短很快
  • 现代 c++20使用的是无栈协程

每次创建协程时,编译器都会自动完成堆分配。
协程的返回类型要求

[[#promise_type 类型]]

  • 协程的返回类型 Result 必须能够匹配 __coroutine_traits_impl<Result> 的模板参数要求.
  • __coroutine_traits_impl<Result> 要求其 返回类型 Result 必须含有 promise_type
简单来说,就是返回值类型 `Result` 含有 `Result::promise_type`
它可以是嵌套类, 或者使用 `using` 引入了 `promise_type` 类型.
`__coroutine_traits_impl` 是什么?
- 它是 `coroutine_traits` 的实现, 也是它的基类
- 它是一个空类型体, 只是用于检查 返回结果是否含有内部类型 `promise_type`
协程的执行流程

协程体的执行

  1. 使用 operator new 分配协程返回类型对象. (该运算可以重载)
  2. 将协程函数实参复制到协程状态对象中, 复制时可以按值传递或按引用传递
  3. 构造 return_type::promise_type 对象
    • 其中 return_type 是协程返回类型
    • 构造 promise_type 对象时, 若它有一个构造函数形参和协程的形参一致, 则会调用那个构造函数, 并传入所有协程的实参; 否则调用默认构造器
  4. 调用 promise_type::get_return_object(), 并将返回值保留在局部变量中, 当协程挂起时, 该值将返回给调用点,
  5. 调用 promise_type.initial_suspend()
    • 然后对返回值进行 co_await 运算.
    • 如果返回值满足挂起条件, 则协程一开始就被挂起
    • 否则进入下一步, 执行协程体
  6. 执行协程体, 直到遇到 co_awaitco_yieldco_return

流程图

Program with c++ 20

![[Pasted image 20241229210902.png]]

1.3 co_await

co_await 运算符和执行流程
  • 用于挂起协程并等待某个异步操作完成
  • 必须和 [[#awaitable 类型的概念|awaitable类型]]一起使用
co_await awaitable_obj;

如果使用默认的 co_await 运算, 那么将自动执行:

  1. 调用 awaitable_obj 的 [[#await_ready()]]
    • 如果返回 false 则表示协程未准备好, 协程将被挂起
    • 如果返回 true 则直接执行协程体
  2. 如果被挂起, 则执行 [[#await_suspend()]], 随后控制权交给调用协程处的程序.
  3. 如果在挂起后被恢复, 则 进入 [[#await_resume()]]
    • await_resume() 的返回值作为 co_await 表达式的结果
    • 这个返回值是协程体需要的
  4. 协程恢复完成, 继续执行 co_await 之后的协程体.
co_await 执行的例子

在调试模式下, 单步执行该程序, 可清楚地看到 co_await 的流程和作用

struct Myawaiter {
	bool await_ready() {                            //5
		std::cerr << "await_ready\n";
		return false;
	}

	void await_suspend(std::coroutine_handle<> h) { //6
		std::cerr << "await_suspend\n"; 
	}

	int await_resume() {                            //8
		std::cerr << "await_resume\n";
		return 44;
	}
};

struct promise_type;

struct RT {
public:
	using promise_type = ::promise_type;
	using handle_type = std::coroutine_handle<promise_type>;

	handle_type _handle; //句柄成员

	handle_type get_handle() { return _handle; }

	~RT() {
	}
};

struct promise_type {
	RT get_return_object() {                      //1
		std::cerr << "get_return_object\n";
		return RT{._handle = std::coroutine_handle<promise_type>::from_promise(*this)};
	}

	std::suspend_always initial_suspend() {       //2
		std::cerr << "initial_suspend\n";
		return {};
	}

	std::suspend_never final_suspend() noexcept { //11
		std::cerr << "final_suspend\n";
		return {}; 
	}

	void return_void() {                          //10
		std::cerr << "return_void\n";
	}

	void unhandled_exception() { std::terminate(); }
};

RT cofun() {
	co_await Myawaiter{};  //4
	co_return;             //9
}

int main() { 
	RT r = cofun();
	RT::handle_type h = r.get_handle();
	h.resume(); //3
	h.resume(); //7
	return 0;
}

/* 打印结果
get_return_object
initial_suspend
await_ready
await_suspend
await_resume
return_void
final_suspend
*/

1.4 co_return

co_return 作用
  • co_return 可以将一个值返回给协程的调用者。 这个值可以是任何类型,包括基本类型、自定义类型、甚至是 void
  • co_return 语句会终止当前协程的执行,并调用 promise_type::final_suspend(), 并将控制权返回给调用者。
  • 最后协程将结束了.
co_return 流程
  1. 当协程执行到 co_return 语句时,它会先计算返回值 (如果有的话), 计算返回值过程如下:
    • 调用 promise::return_value(): co_return 会调用与协程关联的 promise_type 对象的 return_value() 成员函数,并将计算得到的返回值传递给它。
      • promise::return_value() 函数负责处理返回值,例如将其存储在 promise 对象的成员变量中,或者进行其他自定义操作。
      • 如果 promise 对象没有定义 return_value() 函数,或者 co_return 语句没有返回值(例如 co_return;),则不会调用 return_value() 函数。
  2. 调用 promise::final_suspend()
  3. 协程的控制权返回给调用者
    • 调用者可以通过 std::coroutine_handle::promise() 函数获取 promise 对象,并访问其中存储的返回值。
co_return 执行的例子

在调试模式下, 单步执行该程序, 可清楚地看到 co_await 的流程和作用

struct Myawaiter {
	bool await_ready() {
		std::cerr << "await_ready\n";
		return false;
	}

	void await_suspend(std::coroutine_handle<> h) { std::cerr << "await_suspend\n"; }

	int await_resume() {
		std::cerr << "await_resume\n";
		return 44;
	}
};

struct promise_type;

struct RT {
public:
	
	using promise_type = ::promise_type;
	using handle_type = std::coroutine_handle<promise_type>;

	handle_type _handle; //句柄成员

	handle_type get_handle() { return _handle; }

	RT(std::coroutine_handle<promise_type> h) : _handle(h) { 
		std::cerr << "RT 构造函数\n";
	}
	~RT() { 
		std::cerr << "RT 析构函数\n";
	}

	int get_val();
};

struct promise_type {
	int value{};
	RT get_return_object() {
		std::cerr << "get_return_object()\n";
		return RT{std::coroutine_handle<promise_type>::from_promise(*this)};
	}

	std::suspend_always initial_suspend() {
		std::cerr << "initial_suspend()\n";
		return {};
	}

	std::suspend_always final_suspend() noexcept { 
		std::cerr << "final_suspend()\n";
		return {}; 
	}

	void return_value(int x) { 
		std::cerr << "return_value()\n";
		
	}

	void unhandled_exception() { std::terminate(); }

	promise_type() { std::cerr << "promise_type 构造函数()\n"; }

	~promise_type() { std::cerr << "promise_type 析构函数()\n"; 
	}
};


int RT::get_val() { return _handle.promise().value; }

RT cofun() {
	std::cerr << "----------------------------------开始co_await...\n";
	co_await Myawaiter{};
	std::cerr << "----------------------------------结束co_await...\n";

	std::cerr << "----------------------------------开始co_return...\n";
	co_return 1;
	std::cerr << "----------------------------------结束co_return...\n";  // 不会执行
}


int main() { 
	RT r = cofun();
	RT::handle_type h = r.get_handle();
	h.resume();
	h.resume();

	std::cerr << "协程设置的值为:  " << r.get_val() << "\n";
	h.destroy();
	return 0;
}

/* 输出结果

promise_type 构造函数()
get_return_object()
RT 构造函数
initial_suspend()
----------------------------------开始co_await...
await_ready
await_suspend
await_resume
----------------------------------结束co_await...
----------------------------------开始co_return...
return_value()
final_suspend()
协程设置的值为:  0
promise_type 析构函数()
RT 析构函数

*/

1.5 co_yield

co_yield 流程
  1. 当协程执行到 co_yield expression; 语句时,会发生以下事情:
    • expression 被计算,其结果将作为返回值, 返回给调用者
    • 控制权返回给协程的调用者。
    • 协程的执行状态被保存,包括局部变量, 寄存器值等。
  2. 返回值给调用者: 协程的调用者会接收到 co_yield 表达式的结果。
    • promise_type 中的成员变量来保存结果
    • 调用者可以通过一些接口来查询该结果.
  3. 恢复协程执行:当调用者希望继续执行协程时,可以调用 std::coroutine_handle<promise_type>::resume() 函数。
    • 此时,协程会从之前 co_yield 语句中断的地方恢复执行。
    • 协程可以使用 co_await 关键字等待异步操作完成,也可以使用 co_return 返回最终结果。
todo

也是一个 C++ 协程中的关键字
用于暂停协程的执行并返回值给调用者。
每次调用 co_yield 都会产生一个挂起点, 并调用函数 promise_type::yield_value()

co_yield 相关的函数实际上是在 promise 对象中定义的,用于处理 co_yield 的行为。 主要的函数有:

  1. yield_value():

    • 作用: 处理 co_yield 表达式的值。
    • 参数: co_yield 表达式的值会作为参数传递给 yield_value() 函数。
    • 返回值: yield_value() 函数的返回值没有特殊意义,因为协程在 co_yield 处已经暂停了。
    • 示例:
struct MyPromise {
  // ...
  std::suspend_always yield_value(int value) {
    // 处理 co_yield 传递的值
    currentValue = value;
    std::cout << "Yielded value: " << value << std::endl;
    return {}; // 返回 std::suspend_always 表示挂起协程
  }
  // ...
};
  1. await_transform():
    • 作用: await_transform() 本身不是专门为 co_yield 服务的,但它可以与 co_yield 配合使用,对 co_yield 返回的对象进行自定义处理。
    • 参数: co_yield 表达式的值会作为参数传递给 await_transform() 函数。
    • 返回值: await_transform() 函数的返回值决定了协程的行为:
      • 返回一个 awaitable 对象:协程会等待该 awaitable 对象完成后再继续执行。
      • 返回 std::suspend_always:协程会立即挂起。
      • 返回 std::suspend_never:协程会继续执行,不会挂起。
    • 示例:
struct MyPromise {
  // ...
  std::suspend_always await_transform(int value) {
    // 处理 co_yield 传递的值
    std::cout << "Custom processing: " << value << std::endl;
    return {}; // 挂起协程
  }
  // ...
};

总结:

  • co_yield 不是函数,而是一个用于暂停协程并返回值的关键字。
  • yield_value() 函数用于处理 co_yield 表达式的值。
  • await_transform() 函数可以与 co_yield 配合使用,对 co_yield 返回的对象进行自定义处理。

例子

#include <iostream>
#include <coroutine>

// 定义 promise 类型,用于自定义协程的行为
struct GeneratorPromise {
  int current_value;

  std::suspend_always initial_suspend() { return {}; }
  std::suspend_always final_suspend() noexcept { return {}; }
  void return_void() {}
  void unhandled_exception() {}

  // 用于获取协程返回的对象
  GeneratorPromise& get_return_object() { return *this; }

  // 用于处理 co_yield 表达式
  std::suspend_always yield_value(int value) {
    current_value = value;
    return {}; 
  }
};

// 定义协程类型
struct Generator {
  using promise_type = GeneratorPromise;
  
  coroutine_handle<promise_type> handle;

  Generator(std::coroutine_handle<promise_type> h) : handle(h) {}
  ~Generator() { if (handle) handle.destroy(); }

  // 获取 co_yield 返回的值
  int next_value() { return handle.promise().current_value; }
};

// 定义一个生成从 0 到 max 的整数序列的协程
Generator generate_numbers(int max) {
  for (int i = 0; i <= max; ++i) {
    co_yield i; 
  }
}

int main() {
  // 创建协程
  auto generator = generate_numbers(5);

  while (generator.handle()) { //当协程句柄有效时, 则可以继续
    std::cout << generator.next_value() << " ";
    generator.handle().resume(); //继续协程
  }

  std::cout << std::endl; 
  return 0;
}

附录: 一些在协程中的重要类型

总结

如何在外部获取协程句柄?
如何在外部获取和协程关联的 promis_type 对象?
如果在外部获得协程的返回值?

awaiter 类型和它的成员函数

awaiter 类型

等待器类型可被用于 co_await 运算.
必须含有三个可见的成员函数:

  • await_ready
  • await_suspend
  • await_resume

当对一个 awaiter 类型进行 co_await 运算时, 将依次发生如下:

  1. 调用 await_ready(), 根据返回值决定是否挂起
    • 返回 true, 不挂起, 则继续执行协程体
    • 返回 false, 挂起, 则执行 await_suspend(), 此时协程体本身被阻塞
  2. 当进入 await_suspend 后, 可以在适当的时候进入 await_resume, 继续执行协程体
await_ready() 成员
  • 它返回 bool 值, 用于检查并判断是否需要挂起协程.
    • 返回 true:表示协程可以直接继续执行 (已经准备好了),不需要挂起
    • 返回 false:表示协程需要挂起, 一旦挂起, 将进入 await_suspend
await_suspend() 成员
  • await_ready 返回 false 导致协程被挂起时, 将调用这个函数
  • 它接收一个 [[#std coroutine_handle 类型]] 的参数
    • 这个参数实际上是当前协程的句柄,它指向了这个协程的执行上下文.
    • 它的实参自动传递, 并不需要手动传递

await_suspend 的返回类型有多种

  • 返回 void 表示当前协程挂起之后, 将执行权还给当初调用点
  • 返回 true,表示当前协程挂起之后, 将恢复当前协程的函数
  • 返回 false,则恢复执行当前协程
    • 注意此时不同于 返回 true 的情形,此时协程已经挂起,await_suspend 返回 false 相当于挂起又立即恢复
  • 返回其他协程的 coroutine_handle 对象,这时候返回的 coroutine_handle 对应的协程被恢复执行
  • 抛出异常,此时当前协程恢复执行,并在当前协程当中抛出异常

例子

class awaiter{
public:
	await_ready(){return false;} //需要挂起
	
	void await_suspend(coroutine_handle<> h){ //传入句柄
	}

	constexpr bool await_suspend(coroutine_handle<> h){
		std::cerr<<"await_suspend\n";	
		return false; //
	}
}

struct ReturnType;

ReturnType mycorotine(){
	
}
await_resume() 成员
  • 当协程恢复执行时调用
  • 它的返回值作为 co_await 表达式的结果
suspend_never 和 suspend_alaways 类型

源码

struct suspend_never {
  constexpr bool await_ready() const noexcept { return true; }
  constexpr void await_suspend(coroutine_handle<>) const noexcept {}
  constexpr void await_resume() const noexcept {}
};

struct suspend_always{
	constexpr bool await_ready() const noexcept { return false; }
	constexpr void await_suspend(coroutine_handle<>) const noexcept {}
	constexpr void await_resume() const noexcept {}
};

suspend_neverawait_ready 总是返回 true, 这将总是挂起协程.
这里的 coroutine_handle<> 实际上是类型 coroutine_handle<void>

例子

struct promise_type{
	//...
	
	std::suspend_never initial_suspend(){
		std::<<"不会挂起的\n";
		return {};
	}
};

awaitable 类型

awaitable 和 awaiter 的联系
  • 可以进行 co_await 运算的类型 都称为 awaitable 类型
  • awaitable 类型包含了 awaiter 类型
  • 也包含那些通过 重载 co_await 运算, 从而可进行 co_await 运算的类型
  • 或者可以一步 隐式转换为 可进行 co_await 运算的类型
`awaitable` 是一个概念, 是一种接口或约定
`awaiter` 这是 `awaitable` 的具体实现

例子

class Awaiter_String: public std::string{
	using std::string::string;
	
};
class Awaitable_String : public std::string{
}

std::coroutine_handle 类型

coroutine_handle 类型介绍
  • 是一个类型模板, 定义在 corotine 头文件中
  • 模板实参一般是 [[#promise_type 类型]], 或者为空
  • 通过该类型的对象, 可以手动管理协程的生命周期
template <typename _Promise>
struct coroutine_handle{
	//...
};
coroutine_handle 的常见成员函数

通过句柄的一些成员函数, 可以控制协程的状态.

源码

constexpr explicit operator bool() const noexcept{ 
	return bool(_M_fr_ptr); 
}

bool done() const noexcept { return __builtin_coro_done(_M_fr_ptr); }

void operator()() const { resume(); }

void resume() const { __builtin_coro_resume(_M_fr_ptr); }

void destroy() const { __builtin_coro_destroy(_M_fr_ptr); }

解释

  • resume():用于恢复协程的执行。如果协程已经完成或销毁,则行为未定义。
  • destroy():销毁协程,这会清理协程的状态并释放资源。应当在协程不再需要时调用。
  • done():返回一个布尔值,指示协程是否已经完成
  • operator() 实际上也是调用 resume 恢复协程
`__builtin_coro_destroy`, `__builtin_coro_resume` 等这些都是编译器内置的函数, 没有源码, 不具备可移植性
获取协程的句柄

由于协程在开始时, 就构造了返回对象, 以及 promise_type 对象.
因此这个 promise_type 对象就和协程对应. 可以从 promise_type 定位协程.
那么理应从 promise_type 对象可以获取 协程句柄.

根据上面思想, 协程库的 coroutine_handle 有如下静态函数 from_promise
只要传入一个 promise_type 对象, 该函数可以构造并返回协程的句柄.

//源码
template <typename _Promise> 
struct coroutine_handle {
//...

  static coroutine_handle from_promise(_Promise &__p) {
    coroutine_handle tmp;
    tmp._M_fr_ptr = 
    __builtin_coro_promise( 
						    (char *)&__p, 
						    __alignof(_Promise), 
						    true
						  );
    return tmp;
  }
}
从句柄获取协程的 promise_type 对象

使用 coroutine_handlepromise() 成员, 可以获取与协程句柄对应的 promie_type 对象.

//源码
template <typname _Promise>
struct coroutine_handle{
    _Promise& promise() const {
        return *static_cast<_Promise*>(
	        __builtin_coro_promise(this->__handle_,
							       alignof(_Promise),
							       false
							       )
								      );
    }
};

promise_type 类型

  • 协程的返回类型为 RT ,则 RT 内必须含有类型 RT::promise_type
  • promise_type 是自定义类型.
  • 协程有时候不必直接返回一个对象 (不必写一个 return 语句)
    • 因为协程的返回对象在协程开始时 已经构建好了
    • 即使不做任何返回, 协程的状态区域也保存着返回值.
    • promise_type 可以定义一个 get_return_object() 成员, 它可以构造返回对象.
struct myPromise{ //在返回类型外部定义 promise_type
	//...
};

struct Return_Type{
	using promise_type = myPromise;
	friend promise_type;
	//...
};
get_return_object()
  • 这个成员函数是必须定义的,它负责创建协程的返回值 RT 类型对象
  • 它的返回类型必须是 协程返回类型 RT
  • 在协程一开始时, 它就会调用 get_return_object(), 构造这个对象. 并返回给协程的调用点

例子

struct RT{
	struct promise_type{
		RT get_return_object(){//...
		}
		//...
	}
}

RT cofunc(){ //协程函数
	//
}

int main(){
	RT r = cofunc(); // 协程一开始就构建好了这个对象r, 这个对象一般包含着协程句柄信息.
}


initial_suspend()

当协程一开始时, 实际将发生如下调用

co_await return_type::promise_type.initial_suspend();
  • initial_suspend() 函数返回一个 [[#awaitable 类型]] 的对象,
    • 返回值一般是 std::suspend_neverstd::suspend_always
      • 返回 std::suspend_never 表示不挂起
      • 返回 std::suspend_always 或自定义的 awaiter 则表示挂起
  • 然后这个对象参与 [[#co_await 流程]] 运算
`suspend_never` 和 `suspend_always` 是什么?
- 它们都是 awaiter 类型, 参看下面源码
- `suspend_never`中 ,`await_ready`函数返回`true`, 表示准备好了,不会挂起
final_suspend()

这个成员函数在协程执行到最后(即遇到 co_return 或函数结束)时被调用
它的返回值决定协程是否在结束前挂起.

struct final_awaiter{
	bool await_ready(){ return false;}
	void await_suspend(coroutine_traits<> h){
		h.destroy();
	}
	void await_resume(){}
};


struct promise_type{
	//...
	
	final_awaiter final_suspend(){
		return {};
	}
};
unhandled_exception()

当协程内部抛出未捕获的异常时,会调用这个函数

return_void() 和 return_value()

协程的返回类型

在下文中, 我们使用 Return_type 或者 RT 指代返回类型

coroutine_traits 类型 和 协程返回类型要求

源码

// C:\msys64\mingw64\include\c++\14.2.0\coroutine
template <typename _Result, typename... _ArgTypes>
struct coroutine_traits;

template <typename _Result, typename = void>
struct __coroutine_traits_impl {};

template <typename _Result>
	// 用requires子句和表达式来约束_Result类型, 必须含有 promise_type
	requires requires { typename _Result::promise_type; } 
struct __coroutine_traits_impl<_Result, void>{
  using promise_type = typename _Result::promise_type;
};

template <typename _Result, typename... _ArgTypes>
struct coroutine_traits : __coroutine_traits_impl<_Result> {};
  • coroutine_traits 从给定的协程返回类型 _Result 中提取出 promise_type
  • 它只用于检查协程体的 返回类型 是否合格
  • 编译器会根据协程的返回类型 _Result 自动推导出该类型的 promise_type,并使用它来进行协程的管理

标签:co,入门,await,return,promise,协程,type,21
From: https://www.cnblogs.com/easify/p/18642077

相关文章

  • 16. C++快速入门--模板和Concept
    待修改1定义模板1.1模板形参模板参数模板可以有两种参数,一种是类型参数,一种是非类型参数这两种参数可以同时存在,非类型参数的类型可以是模板类型形参template< typenameT,//1 Ta//2>第一个参数是类型参数T第二个是非类型参数a,它的类型和形参......
  • Ajax入门以及Axios的详细使用(含Promise)
    1.概述1.1是什么Ajax=AsynchronousJavaScriptandXML(异步的JavaScript和XML)Ajax不是新的编程语言,而是一种用于创建快速动态网页的技术Ajax最大的优点是在不重新加载整个页面的情况下,可以与服务器交换数据并更新部分网页内容,使网页实现异步更新传统的网页(......
  • [笔记]Airflow入门
    前言Airflow入门教程正文简介任务管理、调度、监控工作流平台。基于DAG(有向无环图)的任务管理系统。基本架构组件scheduler:以有向无环图(dag)的形式创建任务工作流,根据用户的配置将任务定时/定期进行调度worker:任务的执行单元,worker会从任务队列当中拉取任务并运行......
  • docker自学入门教程
    目录1docker的安装1.1卸载旧版本docker1.2下载相关依赖1.3配置docker的安装源地址1.4安装docker1.5设置开机启动1.6简单命令检验是否安装成功1.7配置镜像加速器(官网属于国外网站,下载镜像会比较慢,推荐阿里云)1.8docker官网的镜像仓库地址2docker基......
  • 使用AI21 Chat模型进行自然语言处理
    老铁们,今天我们聊聊如何上手AI21的聊天模型。AI21提供的模型在不同场景下支持不同的参数,详细参数可以直接查看AI21文档。另外,对于AI21在LangChain中的组件,你也可以了解一下更多细节。技术背景介绍随着自然语言处理(NLP)技术的快速发展,越来越多的AI语言模型被应用到各个领域......
  • 零基础python入门要多久?python怎么学?python就业前景如何?看这篇就够了
    包含编程籽料、学习路线图、爬虫代码、安装包等!【点击领取】Python入门需要要多久?作为一门简单高效、对新手友好的一门编程语言,入门只需要花费2-4周就够了。从入门到精通,花费3-4个月也就够了。但是也不能一概而论,因为总是会有人在学习的过程中做无用功,所以会花费更长的时......
  • java之mybatis框架第一天-mybatis入门
    1.前言什么是mybatisMyBatis是一款优秀的持久层框架,用于简化JDBC的开发。MyBatis本是Apache的一个开源项目iBatis,2010年这个项目由apache迁移到了googlecode,并且改名为MyBatis。2013年11月迁移到Github。2.mybatis快速入门目标:使用Mybatis查询所有用户数据(1)准备工......
  • leetcode 213. 打家劫舍 II
    213.打家劫舍II与  198.打家劫舍  相比,多了首和尾不能同时偷的条件但是没写出来......
  • 大模型入门书籍 | 《大模型基础》开源分享!
    一、书籍介绍由浙江大学DAILY实验室毛玉仁研究员、高云君教授领衔撰写的《大模型基础》教材第一版。这本教材为对大语言模型感兴趣的读者系统地讲解相关基础知识、介绍前沿技术。本书包括传统语言模型、大语言模型架构、提示工程、高效参数、模型编辑、搜索增强增加生成......
  • yolo数据集 - 2130张边坡排水沟堵塞数据集分享 - 无人机采集与数据增强处理
    项目概述本篇文章分享了一个yolo数据集,该数据集包含了2130张边坡排水沟堵塞的图像,图像均来自无人机采集,为高精度的边坡排水沟堵塞问题提供了宝贵的图像数据支持。数据集特点图像来源:所有图像均由无人机进行高空采集,确保了数据集的广泛性和代表性,涵盖了多种自然环境中的......