惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
C
CXSECURITY Database RSS Feed - CXSecurity.com
D
Docker
有赞技术团队
有赞技术团队
WordPress大学
WordPress大学
Jina AI
Jina AI
小众软件
小众软件
Last Week in AI
Last Week in AI
Hugging Face - Blog
Hugging Face - Blog
博客园 - 三生石上(FineUI控件)
宝玉的分享
宝玉的分享
美团技术团队
爱范儿
爱范儿
V
V2EX
大猫的无限游戏
大猫的无限游戏
人人都是产品经理
人人都是产品经理
J
Java Code Geeks
博客园 - 司徒正美
博客园 - 叶小钗
S
SegmentFault 最新的问题
量子位
S
Secure Thoughts
月光博客
月光博客
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
O
OpenAI News
L
LINUX DO - 最新话题
罗磊的独立博客
SecWiki News
SecWiki News
雷峰网
雷峰网
Recent Announcements
Recent Announcements
V2EX - 技术
V2EX - 技术
T
Tailwind CSS Blog
H
Hacker News: Front Page
Exploit-DB.com RSS Feed
Exploit-DB.com RSS Feed
云风的 BLOG
云风的 BLOG
Schneier on Security
Schneier on Security
T
The Blog of Author Tim Ferriss
IT之家
IT之家
博客园 - 聂微东
腾讯CDC
N
News | PayPal Newsroom
P
Proofpoint News Feed
CTFtime.org: upcoming CTF events
CTFtime.org: upcoming CTF events
The GitHub Blog
The GitHub Blog
Hacker News: Ask HN
Hacker News: Ask HN
aimingoo的专栏
aimingoo的专栏
Webroot Blog
Webroot Blog
Application and Cybersecurity Blog
Application and Cybersecurity Blog
Google DeepMind News
Google DeepMind News
K
Kaspersky official blog

博客园 - keleman

lambda 如何捕获 所在子线程ID 配置 git 到达 通过 git &&ssh 登录 gitee.com 和 github.com 整理于豆包问答,测试可以实现 持有 std::unique_ptr 的 lambda → 不能放进 std::function 从豆包整理 C++ Lambda 捕获方式・安全写法速查表 整理自豆包 C++14 Lambda 相比 C++11 的核心改进 firefox 149 在 linux mint 21 上的 mozillia 源安装 qt GUI 如何利用q thread 和qfuturwatcher 实现 不阻塞主线程? xargs 和管道 |的区别 一加5t的volte怎么设置 做上位机软件设计 下位机软件设计 qt 将槽函数在子线程中执行的方法 以二级指针作为函数参数,并从函数中分配空间的原理 std::forward<T> ,std::move 使用场景的区别 VS2017 设置 类模板参数推导(CTAD, Class Template Argument Deduction) postEvent 和 sendEvent的区别 QObject Event( )customEvent( ) 函数区别 关于实际项目中必要的几个文件操作接口 QwaitCondition.的工作原理 【Debian】更换软件 源出现的Certificate问题解决方法 linux PS1 提示符的颜色设置 python 的几个常用的函数 知识点 设置 docker 国内的 映像加速器 docker 容器安装软件 docke 容器设置代理 docker 安装问题
由QThread::currentThread()引出的 如何在线程中安全运行任务 由豆包整理的
keleman · 2026-05-26 · via 博客园 - keleman

这段代码的完整意图
void execTask(std::function<void()> task)
{
// 如果当前就在对象线程里---1
if(QThread::currentThread() == T::thread())
{
task(); // 直接执行
}
else
{ // 不在对象线程 → 投递到对象线程执行
QMetaObject::invokeMethod(this, [=]() { task(); }, Qt::QueuedConnection);
}
}

为什么要这么写?(Qt 核心规则###

Qt 有一条铁律:
QObject 对象的所有非 const 成员函数,必须在它所属的线程中调用!
否则会:
信号槽失效
界面卡死
程序崩溃
多线程竞争、
数据错乱

所以这段代码的目的是:
安全地执行任务,确保任务一定在对象归属线程中运行
作用:无论从哪个线程调用 execTask,task 都会安全地在对象归属线程执行。
if(QThread::currentThread() == T::thread())
意思:判断当前代码是否运行在【这个对象所属的线程】中。

常用跨线程调用方式

先记住一句话
所有跨线程执行的核心只有一个:
把 “要执行的函数” 投递到目标线程的事件循环里。
下面这些方法本质都是干这件事。
一、信号槽(最常用、最简单)
这是 Qt 最推荐、最安全、代码最干净的方式。

// 在对象线程的类里定义一个槽函数
class Worker : public QObject
{
Q_OBJECT
public slots:
void doWork() {
// 这里一定运行在对象线程
}
};

// 跨线程调用
emit signalToWork(); // 连接时用 Qt::QueuedConnection

关键点
只要连接方式是 Qt::QueuedConnection(跨线程自动默认)
槽函数百分百在接收者所属线程执行
不需要手动判断线程
优点
最简单
最安全
支持参数传递
不会有野指针风险
二、QTimer::singleShot (0, ...)(超实用技巧)
这是 Qt 里隐藏的神技,很多老程序员都用它。
用法
cpp
运行

QTimer::singleShot(0, this,[this]() {
// 这里一定运行在 this 所属线程
});

原理

delay=0 不代表立刻执行
而是把任务压入事件队列尾部
事件循环在对象线程里跑 → 函数就在对象线程执行

优点

一行代码
不需要信号槽
不需要 invokeMethod
支持 Lambda

三、子类化 QThread + run () + exec ()(传统方式)
这是 Qt 最经典的线程写法,但不推荐直接在 run 里写业务。
用法
cpp
运行

class MyThread : public QThread
{
void run() override {
// 这里是新线程
Worker worker; // 该对象属于新线程
exec(); // 开启事件循环
}
};

特点

run() 内部就是新线程
你在 run() 里创建的对象都属于该线程
必须调用 exec() 才能让信号槽 /postEvent 生效

缺点

不灵活
不适合频繁切换任务

四、moveToThread + 自定义触发(最标准架构)
这是 Qt 官方最推荐的多线程架构。
用法
cpp
运行

QThread* thread = new QThread;
Worker* worker = new Worker;
worker->moveToThread(thread); // 把对象丢进线程

thread->start();
// 之后所有 worker 的槽、事件、invokeMethod 都在新线程执行

优点

线程与逻辑解耦
最稳定
适合长期后台任务

五、QtConcurrent::run ()(简单异步,不控制线程)
适合一次性后台任务,不关心具体线程。
用法
cpp
运行

QtConcurrent::run( {
// 这里在线程池里运行
});

特点

自动使用线程池
不需要手动创建 QThread
不能精确控制哪个线程

缺点

不能和 QObject 事件循环配合
不能安全更新 UI

总结:跨线程执行的 6 种方法

我给你做一张最实用速查表:

  1. 信号槽(Qt::QueuedConnection) → 最常用、最安全
  2. QMetaObject::invokeMethod → 最灵活、支持 Lambda
  3. postEvent → 底层机制,适合自定义事件
  4. QTimer::singleShot(0, ...) → 最简单的 Lambda 投递
  5. moveToThread + 槽函数 → 官方标准架构
  6. QtConcurrent::run → 一次性后台任务

所有 Qt 里真正能 “切换线程执行代码” 的方法一次性讲全,包含冷门但合法的方法,但只讲能安全用在项目里的,不玩花活。

我会分成:标准方法(你已知) + 冷门正统方法(你未知) + 绝对不能用的方法(避坑)

  1. QObject::event () + 自定义事件(底层正统)
    这是 Qt 所有线程切换的底层源头,postEvent 就是基于它。
    你可以自己定义事件,发出去后,一定会在对象线程里执行。
    class MyEvent : public QEvent
    {
    public:
    std::function<void()> func;
    MyEvent(std::function<void()> f) : QEvent(QEvent::User), func(std::move(f)) {}
    };

// 在 TaskExecuter 里重写 event
bool event(QEvent e) override
{
if (e->type() == QEvent::User) {
auto me = static_cast<MyEvent
>(e);
me->func();
return true;
}
return T::event(e);
}

// 切换线程
void runInThread(std::function<void()> f)
{
qApp->postEvent(this, new MyEvent(std::move(f)));
}

✅ 优点:最底层、最稳定
✅ 完全可控
2. Qt::BlockingQueuedConnection(阻塞式跨线程调用)
这是同步版跨线程调用:
发出去 → 等待对方线程执行完 → 再回来。

QMetaObject::invokeMethod(this, task, Qt::BlockingQueuedConnection);

适用场景:
需要等待线程执行结果
必须同步获取返回值
⚠ 注意:不能在目标线程已死锁时调用。
3. QApplication::postEvent + 自定义事件(全局投递)
和第 1 种一样,只是通过全局对象投递。
qApp->postEvent(this, new MyEvent([]{ /* 运行在对象线程 */ }));
4. QThread::exec () 事件循环 + 自定义队列任务
你自己维护一个任务队列,然后用事件唤醒执行。
// 线程里跑一个循环
while (!quit) {
processTasks(); // 执行任务
exec(); // 等待事件
}

这是手写线程工作器,Qt 源码大量使用。
5. QFuture + QFutureWatcher(异步结果回调)
适合需要返回值的跨线程执行。
QFuture future = QtConcurrent::run([]{ return 123; });
QFutureWatcher watcher;
connect(&watcher, &QFutureWatcher::finished, this, &{
// 这里运行在 this 线程
int res = future.result();
});
watcher.setFuture(future);

✅ 优点:支持返回值、线程安全、自动切换
三、不推荐但理论上可以切换线程的方法(尽量别用)

  1. 直接调用 QObject 成员(极危险)
    obj->mySlot(); // 跨线程直接调用 → 崩溃、数据混乱

❌ 禁止!
2. std::thread + 手动同步(不兼容 Qt 事件)
cpp
运行

std::thread([obj]{ obj->doWork(); }).detach();

❌ 不进入 Qt 事件循环,QObject 不安全
四、最终总结:Qt 里所有合法切换线程的方法
我给你一个最全清单,你以后遇到任何 Qt 线程问题都能对照:

信号槽(QueuedConnection)
QMetaObject::invokeMethod
QTimer::singleShot(0, ...)
QCoreApplication::postEvent
自定义 QEvent + event () 重写
moveToThread + 事件循环
BlockingQueuedConnection(同步等待)
QFutureWatcher(异步结果回调)
手写任务队列 + 事件唤醒

以上 9 种是 Qt 世界里全部真正安全、可用、官方支持的线程切换方法。
五、如果你要最强大、最通用、最简洁的写法
我直接给你最终版万能线程切换函数,任何 Qt 项目都能直接用:
void runInObjectThread(std::function<void()> task)
{
if (QThread::currentThread() == this->thread()) {
task();
} else {
QMetaObject::invokeMethod(this, std::move(task), Qt::QueuedConnection);
}
}

这行是Qt 工业界标准万能写法,覆盖 100% 场景。
最终总结
你问的是:
除了你之前讲的,还有哪些?
我给你的新增真正可用方法是:

自定义事件 + postEvent(底层正统)
BlockingQueuedConnection(同步跨线程)
QFuture + QFutureWatcher(带返回值)
手写任务队列 + 事件唤醒

这 4 个是标准、安全、官方,且之前没讲过的。