实用指南:【Linux】多线程创建及封装

news/2025/10/22 12:23:52/文章来源:https://www.cnblogs.com/slgkaifa/p/19157653

实用指南:【Linux】多线程创建及封装

 本篇主要介绍多线程,以及线程的封装,线程控制的相关介绍在【Linux】线程控制

1.创建和等待多线程

我们可以一次创建十个线程,创建的时候直接用for循环创建,参数就传这些线程的名字,然后把这些线程的ID保存在vector里,ID的类型pthread_t其实就是long int类型。

#include 
#include 
#include 
#include 
#include 
#include 
const int num = 10;
void* ThreadFunc(void* args)
{std::string name = static_cast(args);while(true){std::cout << name << "运行中..." << std::endl;sleep(1);}return nullptr;
}
int main()
{std::vector tids;for(int i = 0; i < num; i++) //创建10个线程{char name[64];snprintf(name, sizeof(name), "新线程%d", i); //格式化名字pthread_t tid;int n = pthread_create(&tid, nullptr, ThreadFunc, name);if(n == 0)tids.push_back(tid); //创建成功就把新线程的ID存着elsecontinue;sleep(1);}return 0;
}

然后用监控脚本看一下线程。

 while :; do ps -aL ; sleep 1; done

在等待的时候,通过我们保存在vector里的线程ID一个一个等待。

#include 
#include 
#include 
#include 
#include 
#include 
const int num = 10;
void *ThreadFunc(void *args)
{std::string name = static_cast(args);int cnt = 5;while (cnt--){std::cout << name << "运行中..." << std::endl;sleep(1);}return nullptr;
}
int main()
{std::vector tids;for (int i = 0; i < num; i++) // 创建10个线程{char name[64];snprintf(name, sizeof(name), "新线程%d", i); // 格式化名字pthread_t tid;int n = pthread_create(&tid, nullptr, ThreadFunc, name);if (n == 0)tids.push_back(tid); // 创建成功就把新线程的ID存着elsecontinue;sleep(1);}for (int i = 0; i < num; i++) //一个一个等待{int n = pthread_join(tids[i], nullptr);if (n == 0)std::cout << "等待成功" << std::endl;elsestd::cout << "等待失败" << std::endl;}return 0;
}

但是我们如果在创建线程时for循环里不加sleep,而在ThreadFunc函数最开始加上sleep,会出现如下结果,只有新线程9。

const int num = 10;
void *ThreadFunc(void *args)
{sleep(1);std::string name = static_cast(args);int cnt = 5;while (cnt--){std::cout << name << "运行中..." << std::endl;sleep(1);}return nullptr;
}
int main()
{std::vector tids;for (int i = 0; i < num; i++) // 创建10个线程{char name[64];snprintf(name, sizeof(name), "新线程%d", i); // 格式化名字pthread_t tid;int n = pthread_create(&tid, nullptr, ThreadFunc, name);if (n == 0)tids.push_back(tid); // 创建成功就把新线程的ID存着elsecontinue;//sleep(1);}return 0;
}

因为name是for循环里的一个临时数组,创建线程的时候传递过去的name是这个数组的起始地址,每一次for循环,编译器都会在同一个栈帧的固定偏移量位置上开辟name的空间,就导致每次for循环不停的开辟、释放、开辟、释放...但是每次开辟到的都是同一个位置,所以给pthread_create传过去的name起始地址就是同样的。

ThreadFunc函数拿到这个值之后sleep一秒才将ThreadFunc里的name改动。

所以就发生了ThreadFunc里的name还没来得及改动,创建线程的for循环就进入下一个循环了,里面的name的内容就被修改了,意思就是指针没变,但是指针指向的内容已经改了

所以这里我们要做如下改动,在堆区给name开辟空间,这样每个线程得到的name就不是一样的了。

#include "Thread.hpp"
const int num = 10;
void *ThreadFunc(void *args)
{sleep(1);std::string name = static_cast(args);int cnt = 5;while (cnt--){std::cout << name << "运行中..." << std::endl;sleep(1);}return nullptr;
}
int main()
{std::vector tids;for (int i = 0; i < num; i++) // 创建10个线程{//char name[64]; //错误写法char* name = new char[64]; //正确写法,堆区开辟空间snprintf(name, 64, "新线程%d", i); // 格式化名字pthread_t tid;int n = pthread_create(&tid, nullptr, ThreadFunc, name);if (n == 0)tids.push_back(tid); // 创建成功就把新线程的ID存着elsecontinue;}for (int i = 0; i < num; i++) //一个一个等待{int n = pthread_join(tids[i], nullptr);if (n == 0)std::cout << "等待成功" << std::endl;elsestd::cout << "等待失败" << std::endl;}return 0;
}

2.线程封装

先做一下准备工作。

//Thread.hpp文件
#ifndef _THREAD_H_
#define _THREAD_H_
#include 
#include 
#include 
#include 
#include 
#include 
namespace MyThread
{class Thread{public:private:};
}
#endif
//Main.cc文件
#include "Thread.hpp"
int main()
{return 0;
}
//Makefile
thread:Main.ccg++ -o $@ $^ -lpthread
.PHONY:clean
clean:rm -f thread

我们需要设计一下线程的名字,用一个变量代表被创建的线程编号,类成员变量如下。

namespace MyThread
{static int num = 1;class Thread{public:Thread(): _tid(0),_isdetach(false), /*默认不分离*/_isrunning(false){_name = "new thread-" + std::to_string(num++);}private:pthread_t _tid; //线程IDstd::string _name; //线程的名字bool _isdetach; //线程是否设置分离状态bool _isrunning; //线程是否启动};
}

2.1 线程的启动和分离

设计一个Start接口,启动线程。

bool Start()
{int n = pthread_create(&_tid, nullptr, ThreadFunc, nullptr/*暂时先不传参数*/);if(n != 0) //创建失败{std::cerr << "create tread error: " << strerror(n) << std:: endl;return false;}//创建成功
}

这里还要考虑线程分离的情况,线程的分离状态可以在线程启动之前就设置,也可以是线程已经启动了,再设置成分离状态。

private:void SetRun(){_isrunning = true;}
public:void Detach(){if (_isdetach) // 如果已经分离了,直接返回return;if (_isrunning)           // 如果线程此时正在运行pthread_detach(_tid); // 分离线程// 如果此时还没运行,就只设置标志位,Start函数内会调用DetachSetDetach();}void SetDetach(){std::cout << "线程被分离" << std::endl;_isdetach = true;}bool Start(){int n = pthread_create(&_tid, nullptr, ThreadFunc /*暂时未实现*/, nullptr /*暂时先不传参数*/);if (n != 0) // 创建失败{std::cerr << "create tread error: " << strerror(n) << std::endl;return false;}// 线程启动前设置分离if (_isdetach)Detach();SetRun(); // 设置状态为run}

Detach这样实现就可以保证不管是启动前分离还是启动之后再分离都可以,启动前调用Detach函数,两个if都不满足,就只会设置_isdetach 标志位为false,启动后调用Detach,线程分离并且修改标志位。

2.2 线程停止和等待

取消一个线程就是要确保他是在运行的,取消进程之后,标志位也要更新。

    bool Stop(){if (_isrunning){int n = pthread_cancel(_tid);if (n != 0){std::cerr << "cancel tread error: " << strerror(n) << std::endl;return false;}else{_isrunning = false;std::cout << _name << "stop success" << std::endl;}}return true;}

等待进程不管是不是在运行都可以等待,但是如果被分离了就不用等待了,线程等待的结果我们也可以获取一下。

    bool Join(){if (_isdetach){std::cout << "线程已经被分离,join失败" << std::endl;return false;}int n = pthread_join(_tid, &_ret);if (n != 0){std::cerr << "join tread error: " << strerror(n) << std::endl;return false;}else{std::cout << "join success" << std::endl;}return true;}
private:pthread_t _tid;    // 线程IDstd::string _name; // 线程的名字bool _isdetach;    // 线程是否设置分离状态bool _isrunning;   // 线程是否启动void *_ret;        // 线程等待的结果

2.3 新线程入口函数

我们新线程的入口函数不能在类内实现,因为含函数要求参数为void*,但是在类内部的函数第一个参数是隐藏的this指针!!所以这个ThreadFunc函数其实是传了两个参数。

解决方法有很多,这里我选择将ThreadFunc函数设为static,这样就没有this指针了。

static void* ThreadFunc(void* args)
{
}

但是我们并不是为了让线程只执行类内的ThreadFunc,我们创建线程是为了给线程指派任务的,所以任务必须是类外提供的方法。

#include 
class Thread
{using func_t = std::function; //返回值为void,参数为空//...
}

意思就是定义一个函数类型func_t,让线程未来执行我所指定的方法,所以这里类成员变量还要添加一个。

class Thread
{using func_t = std::function; // 返回值为void,参数为空
public:Thread(func_t func): _tid(0),_isdetach(false), /*默认不分离*/_isrunning(false),_ret(nullptr),_func(func){_name = "new thread-" + std::to_string(num++);}//...
private:pthread_t _tid;    // 线程IDstd::string _name; // 线程的名字bool _isdetach;    // 线程是否设置分离状态bool _isrunning;   // 线程是否启动void *_ret;        // 线程等待的结果func_t _func;      // 线程执行的方法
}

所以现在我们创建的线程不是为了执行对应的ThreadFunc函数的,而是让这个线程执行我给他指派的任务。此时我们的ThreadFunc函数里就要对_func进行回调。

    static void *ThreadFunc(void *args){_func(); //回调处理return nullptr; // 返回值不关心}bool Start(){if (_isrunning)return false; // 线程已经启动了就不能重复启动int n = pthread_create(&_tid, nullptr, ThreadFunc, nullptr /*暂时先不传参数*/);if (n != 0) // 创建失败{std::cerr << "create tread error: " << strerror(n) << std::endl;return false;}// 线程启动前设置分离if (_isdetach)Detach();SetRun(); // 设置状态为runreturn true;}

但是!这样写是无法调用回调的,因为ThreadFunc函数此时是static的,没有this指针,也就无法访问当前类成员属性。所以这里ThreadFunc函数传参就要传this指针。

   static void *ThreadFunc(void *args){Thread *self = static_cast(args);self->_func(); //回调处理return nullptr; // 返回值不关心}bool Start(){if (_isrunning)return false; // 线程已经启动了就不能重复启动int n = pthread_create(&_tid, nullptr, ThreadFunc, this);//...}

这样ThreadFunc函数就可以在类内访问回调方法了。

并且我们前面的分离和运行的标志位逻辑可以放在ThreadFunc函数,这样代码的可读性更好。

    static void *ThreadFunc(void *args){Thread *self = static_cast(args);self->SetRun();      // 设置状态为runif (self->_isdetach) // 设置分离self->Detach();self->_func();  // 回调处理return nullptr; // 返回值不关心}bool Start(){if (_isrunning)return false; // 线程已经启动了就不能重复启动int n = pthread_create(&_tid, nullptr, ThreadFunc, this);if (n != 0) // 创建失败{std::cerr << "create tread error: " << strerror(n) << std::endl;return false;}else{std::cout << _name << "create success" << std::endl;}return true;}

2.4 测试

#include "Thread.hpp"
using namespace MyThread;
int main()
{Thread t();return 0;
}

t后面的括号里就要传要执行的方法,这里可以用Lambda表达式。

int main()
{Thread t([](){while(true){std::cout << "新线程运行中..." << std::endl;sleep(1);} }); // 返回值为void,参数为空return 0;
}

此时就由这个Lambda表达式来初始化_func,然后线程对象就被创建出来了。

int main()
{Thread t([](){while(true){std::cout << "新线程运行中..." << std::endl;sleep(1);} }); // 返回值为void,参数为空t.Start();sleep(3);t.Stop();sleep(3);t.Join();sleep(3);return 0;
}

我们还可以设置Detach,先测试启动前分离。

int main()
{Thread t([](){while(true){std::cout << "新线程运行中..." << std::endl;sleep(1);} }); // 返回值为void,参数为空t.Detach();    // 设置分离状态t.Start();sleep(3);t.Stop();sleep(3);t.Join();sleep(3);return 0;
}

再测试一个启动后再分离线程。

int main()
{Thread t([](){while(true){std::cout << "新线程运行中..." << std::endl;sleep(1);} }); // 返回值为void,参数为空t.Start();t.Detach(); // 设置分离状态sleep(3);t.Stop();sleep(3);t.Join();sleep(3);return 0;
}

 2.5 设置和获取线程名称

  • pthread_setname_np pthread_getname_np 是两个⽤于设置和获取线程名称的⾮标准函数(_np 表⽰ "non-portable",即⾮可移植的)。它们通常在 Linux 和 其他⼀些类 Unix 系统中可⽤,⽤于调试和多线程程序的管理。

pthread_setname_np就是传一个线程ID,给这个线程设置个名字,pthread_getname_np就是可以获取指定线程设置的名字。

static void *ThreadFunc(void *args)
{Thread *self = static_cast(args);self->SetRun();if (self->_isdetach)self->Detach();pthread_setname_np(self->_tid, self->_name.c_str()); //设置名字self->_func();return nullptr;
}
#include "Thread.hpp"
using namespace MyThread;
int main()
{Thread t([](){while(true){char buffer[64];pthread_getname_np(pthread_self(), buffer, sizeof(buffer)); //获取名字std::cout << buffer << "运行中..." << std::endl;sleep(1);} }); // 返回值为void,参数为空t.Start();t.Detach(); // 设置分离状态sleep(3);t.Stop();sleep(3);t.Join();sleep(3);return 0;
}

  • 线程名称⻓度限制: 在 Linux 上,线程名称的最⼤⻓度为 16 个字符(包括结尾的 \0 ),如果名称超过这个⻓度,会被截断。
  • 权限: 通常,只有线程⾃⾝可以设置⾃⼰的名称。尝试设置其他线程的名称可能会导致错误。

其实就是把pthread_setname_np设置的名字写入到了线程的局部存储,get的时候就从局部存储里取,局部存储只有线程自己能访问。

下面是Tread.hpp的代码。

#ifndef _THREAD_H_
#define _THREAD_H_
#include 
#include 
#include 
#include 
#include 
#include  //strerror要包含的头文件
#include 
#include 
namespace MyThread
{static int num = 1;class Thread{using func_t = std::function; // 返回值为void,参数为空private:void SetRun(){_isrunning = true;}void SetDetach(){std::cout << "线程被分离" << std::endl;_isdetach = true;}static void *ThreadFunc(void *args){Thread *self = static_cast(args);self->SetRun();      // 设置状态为runif (self->_isdetach) // 设置分离self->Detach();pthread_setname_np(self->_tid, self->_name.c_str()); //设置名字self->_func();  // 回调处理return nullptr; // 返回值不关心}public:Thread(func_t func): _tid(0),_isdetach(false), /*默认不分离*/_isrunning(false),_ret(nullptr),_func(func){_name = "new thread-" + std::to_string(num++);}void Detach(){if (_isdetach) // 如果已经分离了,直接返回return;if (_isrunning)           // 如果线程此时正在运行pthread_detach(_tid); // 分离线程// 如果此时还没运行,就只设置标志位,Start函数内会调用DetachSetDetach();}bool Start(){if (_isrunning)return false; // 线程已经启动了就不能重复启动int n = pthread_create(&_tid, nullptr, ThreadFunc, this);if (n != 0) // 创建失败{std::cerr << "create tread error: " << strerror(n) << std::endl;return false;}else{std::cout << _name << " create success" << std::endl;}return true;}bool Stop(){if (_isrunning){int n = pthread_cancel(_tid);if (n != 0){std::cerr << "cancel tread error: " << strerror(n) << std::endl;return false;}else{_isrunning = false;std::cout << _name << " stop success" << std::endl;}}return true;}bool Join(){if (_isdetach){std::cout << "线程已经被分离,join失败" << std::endl;return false;}int n = pthread_join(_tid, &_ret);if (n != 0){std::cerr << "join tread error: " << strerror(n) << std::endl;return false;}else{std::cout << "join success" << std::endl;}return true;}~Thread(){}private:pthread_t _tid;    // 线程IDstd::string _name; // 线程的名字bool _isdetach;    // 线程是否设置分离状态bool _isrunning;   // 线程是否启动void *_ret;        // 线程等待的结果func_t _func;      // 线程执行的方法};
}
#endif

3.带模板参数的封装

namespace MyThread
{static int num = 1;template  // 模板class Thread{using func_t = std::function; // 返回值为void,参数为类型为Tprivate://...static void *ThreadFunc(void *args){Thread *self = static_cast *>(args); //全部改为模板self->SetRun();if (self->_isdetach)self->Detach();self->_func(self->_data);  // 回调处理,参数为Treturn nullptr;}public:Thread(func_t func, T data): _tid(0),_isdetach(false),_isrunning(false),_ret(nullptr),_func(func),_data(data)  //这个参数就是回调函数的参数{_name = "new thread-" + std::to_string(num++);}//...private:pthread_t _tid;    // 线程IDstd::string _name; // 线程的名字bool _isdetach;    // 线程是否设置分离状态bool _isrunning;   // 线程是否启动void *_ret;        // 线程等待的结果func_t _func;      // 线程执行的方法T _data;           // 传给执行方法的参数};
}
#endif

除了上面给出来的接口要做改变,其他接口都不变。

#include "Thread.hpp"
using namespace MyThread;
void Conter(int num)
{while (num--){std::cout << "新进程运行中..., " << num << std::endl;sleep(1);}
}
int main()
{int cnt = 5;Thread t(Conter, cnt); // cnt是Conter的参数t.Start();t.Join();return 0;
}

这里的cnt是Conter的参数,就类似于pthread_create函数的第四个参数是第三个参数的参数。

现在我们可以传int类型的参数 ,当然也可以传自定义类型,这里就不演示了。

4.多线程

多线程很简单,代码都不用改,就是在Main.cc里直接for循环创建。

#include "Thread.hpp"
#include 
using namespace MyThread;
int main()
{std::vector threads;for (int i = 0; i < 5; i++){Thread t([](){while(true){char buffer[64];pthread_getname_np(pthread_self(), buffer, sizeof(buffer)); //获取名字std::cout << buffer << "运行中..." << std::endl;sleep(1);} }); // 返回值为void,参数为空threads.emplace_back(t);}for (auto &t : threads){t.Start();}sleep(3); //3秒后全部停止并回收for (auto &t : threads){t.Stop();}for (auto &t : threads){t.Join();}return 0;
}

本篇分享就到这里了,我们下篇见~

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/943262.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

非常好的一个财务agent

非常好的一个财务agenthttps://www.zhihu.com/question/1923049104222721873/answer/1953934765909583735

2025年10月抖音代理商榜单:本地推与千川服务能力对比

正在打开抖音企业号后台,却发现“投流按钮”灰色、千川计划审核不过、本地推门店无法挂载,类似的场景每天都在品牌方、连锁门店、代运营公司上演。2025年抖音电商GMV已突破2.5万亿元,平台月活超7.8亿,流量红利仍在…

20232302 2025-2026-1《网络与系统攻防技术》实验二实验报告

1、实验内容使用netcat获取主机操作Shell,cron启动某项任务。 使用socat获取主机操作Shell, 任务计划启动 。 使用MSF meterpreter生成可执行文件(后门),利用ncat或socat传送到主机并运行获取主机Shell。 使用MSF …

2025年10月医用面膜产品推荐:权威对比评测榜揭晓前五强

一、引言 医美术后创面护理与敏感肌修复需求在2025年持续升温,创业者、采购者及终端消费者均面临“安全、有效、合规”三重决策压力:如何控制进货成本、保障临床效果、提升复购效率。本次评估采用动态对比模型,围绕…

【Redis学习】Redis常用数据类型的万字详解 - 教程

pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "Monaco", "Courier New", …

P11024 做题报告

前言 调代码要笑着调 联考题目放在 T3 其实不然 NOIp 放个T2差不多了 具有多个常见的经典 trick,还是有点落实价值的 题意 给出长度为 \(n\) 的数组的终止状态,总共 \(m\) 次操作,每一次将区间 \([l,r]\) 的所有数字…

多模态数据湖技术深化,Data Agent新能力发布!“认知”将决定企业上限

火山引擎AI创新巡展武汉站成功举办,在下午场的「Data +AI专场闭门会」上,近百名来自各行业的企业管理人员和技术专家共同探讨了在AI浪潮下,企业如何跨越数据应用的鸿沟,真正实现既懂数据又懂业务的智能化升级。会上…

云斗 YDR Special# 004 S 模拟赛

, Rank .拜谢大水赛。玩爽了。[YDR Special# 004] YDSP 2025 云斗 CSP-S 二轮(复赛)全真模拟 链接:link 题解:暂无 时间:4h (2025.10.22 07:40~11:40) 题目数:4 难度:A B C D\(\color{#F39C11} 橙\) \(\color{…

实用指南:网络编程之TCP协议

pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "Monaco", "Courier New", …

2025年10月投资纠纷律师推荐:五强榜单对比评测与选择指南

一、引言 在私募基金、信托计划、资管产品快速扩容的当下,投资纠纷已从偶发风险演变为高净值人群、机构投资者、家族办公室乃至创业者必须直面的常规课题。一旦触发兑付危机、估值跳水或管理人失联,资金回收窗口往往…

完整教程:大模型开发 - 04 QuickStart_DeepSeek 模型调用流程源码解析:从 Prompt 到远程请求

完整教程:大模型开发 - 04 QuickStart_DeepSeek 模型调用流程源码解析:从 Prompt 到远程请求2025-10-22 12:06 tlnshuju 阅读(0) 评论(0) 收藏 举报pre { white-space: pre !important; word-wrap: normal !impo…

Web刷题篇-1 [BJDCTF2020]Easy MD5

打开可以看到如下界面: 打开Burp Suite,输入111后点击提交查询,可以看到如下图所示,将其发至Repeater。 切换至Repeater,进行发送后,可以看到在Response中password=md5($pass,true).这个时候就想到md5的万能…

doris集成vertica 数据源catalog

准备:linux编译环境,需要安装好docker,使用docker编译,同时可以运行linux版本的idea idea官方 下载一个社区版本即可。并复制到虚拟机linux环境的任意目录。启动即可。需要为linux配置好mavengithub 编译doris请参…

JUnit 6.0.0发布:Java 17基线、取消API与Kotlin协程支持

JUnit 6.0.0正式发布,统一平台版本并提升最低要求至Java 17。新增Kotlin协程测试支持、取消令牌API、内置Java飞行记录器监听器,采用JSpecify空值注解并迁移至FastCSV库。Vintage模块现已弃用,为JUnit 4用户提供迁移…

2025年10月上海ICL医生推荐榜:王晓瑛领衔五强对比

想在上海做ICL晶体植入,却担心医生经验不足、术后效果不稳、价格不透明?这是不少近视患者共同的焦虑。ICL手术对医生资质、晶体定制、术前评估要求极高,一旦选择失误,可能面临二次手术或视觉质量下降。2025年,上海…

2025年10月消泡剂厂家推荐:权威榜单一网打尽

如果您正在寻找稳定供货、技术响应快、性价比高的消泡剂厂家,大概率会陷入“信息过载”:网页广告繁多、参数口径不一、小厂贴牌混杂,担心样品与批量质量不一致,又怕售后无人跟进。尤其在2025年四季度环保核查趋严、…

详细介绍:老题新解|合法C标识符

详细介绍:老题新解|合法C标识符pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "Monaco&qu…

国产化Excel开发组件Spire.XLS教程:使用Python将TXT文件转换为CSV

在 Python 中处理数据时,将 TXT 文本文件转换为 CSV 是数据分析、报表生成或跨应用共享数据的常见需求。本文将详细介绍如何借助Spire.XLS使用 Python 实现 TXT 文件转 CSV,包括单文件转换、批量转换以及处理不同分隔…

VMware Holodeck 9.0.1.0 发布 - 自动化部署 VCF 实验环境

VMware Holodeck 9.0.1.0 发布 - 自动化部署 VCF 实验环境VMware Holodeck 9.0 - 自动化部署 VCF 实验环境 高效管理虚拟机 (VM) 和容器工作负载,为本地部署的全栈超融合基础架构 (HCI) 提供云的优势。 请访问原文链接…

【AI大模型前沿】HunyuanWorld-Voyager:腾讯开源的超长漫游世界模型,开启3D场景生成新纪元 - 指南

【AI大模型前沿】HunyuanWorld-Voyager:腾讯开源的超长漫游世界模型,开启3D场景生成新纪元 - 指南2025-10-22 11:52 tlnshuju 阅读(0) 评论(0) 收藏 举报pre { white-space: pre !important; word-wrap: normal …