当前位置:首页>学习笔记>brpc 学习笔记(七):bthread_cond_t——协程友好的条件变量

brpc 学习笔记(七):bthread_cond_t——协程友好的条件变量

  • 2026-05-05 18:57:57
brpc 学习笔记(七):bthread_cond_t——协程友好的条件变量
摘要:上一篇文章拆解了 bthread_mutex_t 的实现——一个 unsigned 如何拆成 locked + contended 两个字节,配合 butex 实现协程友好的互斥锁。本文继续向上,拆解基于 butex 构建的另一个核心同步原语:条件变量 bthread_cond_t。它只用一个 bthread_mutex_t* 和一个 int*(序列号 butex)就实现了完整的 wait/signal/broadcast 语义。其中 broadcast 的 butex_requeue 优化尤其精妙——唤醒一个等待者,将其余等待者直接转移到互斥锁的 butex 上,避免了"惊群效应"。本文从源码出发,拆解条件变量的每一个操作。

在上一篇文章中,我们拆解了 bthread_mutex_t——协程友好的互斥锁。我们看到它用一个 unsigned* butex 实现了完整的三态状态机(0/1/3),trylock 只需一次单字节 exchange,lock/unlock 各只需一次原子操作。

现在我们继续向上,看一个更复杂的同步原语:条件变量

条件变量的核心场景是:"等某个条件成立"。一个线程检查条件不满足时,调用 wait 挂起自己;另一个线程让条件成立后,调用 signal 或 broadcast 唤醒等待者。经典的"生产者-消费者"模式就依赖条件变量。

但条件变量有一个天然的需求:wait 时必须先释放互斥锁(让其他线程能修改条件),被唤醒后必须重新获取互斥锁(检查条件是否真的满足)。这意味着条件变量必须与互斥锁紧密配合。

bthread 的条件变量是如何实现这种配合的?让我们从数据结构开始。


一、bthread_cond_t 与 CondInternal

1.1 结构体定义

typedef struct bthread_cond_t {    bthread_mutex_t* m;  // 关联的互斥锁    int* seq;            // 序列号 butexbthread_cond_t;

只有两个字段:

  • m
    :指向一个 bthread_mutex_t,在首次 wait 时绑定,绑定后不可更换
  • seq
    :一个 butex 指针(butex_create_checked<int>() 返回的 int*),作为序列号

这比 pthread 的 pthread_cond_t 简洁得多——pthread 的条件变量内部需要维护一个等待队列,而 bthread 的等待队列由 butex 内部管理。

1.2 CondInternal:内部视角

实现代码将 bthread_cond_treinterpret_cast 为 CondInternal

struct CondInternal {    butil::atomic<bthread_mutex_t*> m;    // 原子化的互斥锁指针    butil::atomic<int>* seq;              // 原子化的序列号};
编译期静态断言保证两者布局完全一致:
BAIDU_CASSERT(sizeof(CondInternal) == sizeof(bthread_cond_t),              sizeof_innercond_must_equal_cond);BAIDU_CASSERT(offsetof(CondInternal, m) == offsetof(bthread_cond_t, m),              offsetof_cond_mutex_must_equal);BAIDU_CASSERT(offsetof(CondInternal, seq) == offsetof(bthread_cond_t, seq),              offsetof_cond_seq_must_equal);

CondInternal 把 m 和 seq 都变成原子类型,这是实现无锁互斥锁绑定的基础。


二、初始化与销毁

2.1 bthread_cond_init

intbthread_cond_init(bthread_cond_t* __restrict c,                      const bthread_condattr_t*) {    c->m = NULL;    c->seq = bthread::butex_create_checked<int>();    *c->seq = 0;    return 0;}

初始化做两件事:

  1. m = NULL
    :互斥锁指针置空,等待首次 wait 时绑定
  2. 创建 seq butex,初始值为 0
    :序列号从 0 开始,每次 signal/broadcast 递增

注意 bthread_condattr_t 是空结构体——当前没有需要配置的属性。

2.2 bthread_cond_destroy

intbthread_cond_destroy(bthread_cond_t* c){    bthread::butex_destroy(c->seq);    c->seq = NULL;    return 0;}

销毁只需归还 seq butex 到 ObjectPool。m 不需要释放——它指向的互斥锁由调用者管理。

这里有一个与 butex 的竞态问题(参见本系列第四篇):butex_destroy 后,可能还有未完成的 butex_wake。ObjectPool 的"永不释放"特性保证了安全——归还后内存仍然有效,最多产生一次无害的虚假唤醒。


三、signal:单次通知

intbthread_cond_signal(bthread_cond_t* c){    bthread::CondInternal* ic = reinterpret_cast<bthread::CondInternal*>(c);    butil::atomic<int>* const saved_seq = ic->seq;    saved_seq->fetch_add(1, butil::memory_order_release);    bthread::butex_wake(saved_seq);    return 0;}

signal 的实现极其简洁,只有两步:

第一步:fetch_add(1) 递增序列号。 这让所有正在等待"旧序列号"的等待者的 butex_wait 条件不再满足——它们要么已经在等待中被唤醒,要么在下次检查时发现值已变。

第二步:butex_wake 唤醒一个等待者。 唤醒 butex 等待队列的队头。

为什么先 fetch_add 再 butex_wake,而不是反过来?因为 butex_wait 的语义是"如果当前值等于期望值才等待"(参见本系列第四篇)。如果先 butex_wake 再改值,被唤醒者可能在值还没改变时就被唤醒,产生虚假唤醒。虽然条件变量本来就允许虚假唤醒(POSIX 语义),但先改值可以减少不必要的等待。

一个关键细节:saved_seq 在 fetch_add 之前保存。原因:

fetch_add 之后,ic(即 c)可能被销毁——调用者可能在另一个线程中执行 bthread_cond_destroy。保存 saved_seq 后,后续操作只使用 saved_seq,不再通过 ic 间接访问。

四、broadcast:广播通知与 butex_requeue 优化

intbthread_cond_broadcast(bthread_cond_t* c){    bthread::CondInternal* ic = reinterpret_cast<bthread::CondInternal*>(c);    bthread_mutex_t* m = ic->m.load(butil::memory_order_relaxed);    butil::atomic<int>* const saved_seq = ic->seq;    if (!m) {        return 0;    }    voidconst saved_butex = m->butex;    // Wakeup one thread and requeue the rest on the mutex.    ic->seq->fetch_add(1, butil::memory_order_release);    bthread::butex_requeue(saved_seq, saved_butex);    return 0;}

broadcast 是条件变量中最精巧的操作。它解决的核心问题是惊群效应

4.1 什么是惊群效应?

考虑一个经典场景:多个消费者等待生产者。broadcast 时,所有消费者被唤醒,它们从 butex_wait 返回后,立刻执行 bthread_mutex_lock_contended 去竞争互斥锁。但只有一个能拿到锁,其余的又阻塞在互斥锁的 butex 上。

这就是"惊群效应"——N 个等待者被唤醒,但只有 1 个能继续运行,其余 N-1 个白白经历了一次"唤醒 → 重新阻塞"的往返。

4.2 butex_requeue 的解决方案

butex_requeue(saved_seq, saved_butex) 做了一件聪明的事(参见本系列第四篇):

  1. 唤醒 seq butex 上的第一个等待者——正常唤醒
  2. 将 seq butex 上的其余等待者直接转移到互斥锁 butex 的等待队列上——不需要唤醒它们
被转移的等待者直接排到互斥锁的等待队列上。当被 signal 唤醒的那个等待者解锁互斥锁时,butex_wake 会按正常流程唤醒下一个——这恰好就是被 requeue 过来的等待者。

效果:N-1 个等待者省去了一次"唤醒 → 重新阻塞"的往返,直接在互斥锁上排队。

4.3 早期返回:m == NULL

if (!m) {    return 0;  // 还没有人 wait 过,互斥锁未绑定}
如果 m 为空,说明还没有人调用过 wait——没有人可以唤醒,直接返回。

4.4 安全性:saved_seq 与 saved_butex

与 signal 类似,saved_seq 和 saved_butex 都在 fetch_add 之前保存。fetch_add 之后不再访问 ic,避免条件变量被并发销毁时的 use-after-free。


五、wait:等待条件变量

intbthread_cond_wait(bthread_cond_t* __restrict c,                      bthread_mutex_t* __restrict m) {    bthread::CondInternal* ic = reinterpret_cast<bthread::CondInternal*>(c);    const int expected_seq = ic->seq->load(butil::memory_order_relaxed);    if (ic->m.load(butil::memory_order_relaxed) != m) {        // bind m to c        bthread_mutex_t* expected_m = NULL;        if (!ic->m.compare_exchange_strong(                expected_m, m, butil::memory_order_relaxed)) {            return EINVAL;        }    }    bthread_mutex_unlock(m);    int rc1 = 0;    if (bthread::butex_wait(ic->seq, expected_seq, NULL) < 0 &&        errno != EWOULDBLOCK && errno != EINTR) {        rc1 = errno;    }    const int rc2 = bthread_mutex_lock_contended(m);    return (rc2 ? rc2 : rc1);}

wait 是条件变量最复杂的操作。它分四步执行:

5.1 第一步:保存期望序列号

const int expected_seq = ic->seq->load(butil::memory_order_relaxed);

在解锁之前,先读取当前序列号。这个值是"等什么"的依据——如果 seq 的值在 wait 期间被改变(由 signal 或 broadcast),butex_wait 就会返回。

5.2 第二步:绑定互斥锁

if(ic->m.load(butil::memory_order_relaxed) != m) {    bthread_mutex_t* expected_m = NULL;    if(!ic->m.compare_exchange_strong(            expected_m, m, butil::memory_order_relaxed)) {        return EINVAL;    }}

条件变量在首次 wait 时与互斥锁绑定。这是一个 CAS 操作

  • 为 NULL → CAS 成功,绑定传入的互斥锁
  • 不为 NULL 且等于传入的互斥锁 → 已经绑定了同一个锁,跳过
  • 不为 NULL 但不等于传入的互斥锁 → CAS 失败,返回 EINVAL

为什么要首次绑定时才绑定? 因为条件变量初始化时还没有互斥锁——互斥锁由调用者管理。等第一次 wait 时才知道用哪个互斥锁。这与 POSIX的 pthread_cond_wait 语义一致:同一个条件变量必须始终使用同一个互斥锁。

5.3 第三步:解锁 + 等待

bthread_mutex_unlock(m);bthread::butex_wait(ic->seq, expected_seq, NULL);

先解锁互斥锁(让其他线程/协程能修改条件),然后调用 butex_wait 等待序列号变化。

butex_wait 的语义(参见本系列第四篇):如果 *seq == expected_seq,挂起当前 bthread/pthread;如果值已经变了(被 signal/broadcast 修改),立即返回。

第三个参数 NULL 表示无限等待。

5.4 第四步:重新加锁

const int rc2 = bthread_mutex_lock_contended(m);return (rc2 ? rc2 : rc1);
被唤醒后,直接调用 bthread_mutex_lock_contended(竞争路径加锁,参见本系列第六篇),重新获取互斥锁。为什么不调用 bthread_mutex_lock?因为解锁操作已经把 butex 清零了,如果此时 trylock 成功,是最理想的情况;即使失败,lock_contended 内部的 exchange(3) + butex_wait 也能正确处理。

5.5 错误处理

if (bthread::butex_wait(ic->seq, expected_seq, NULL) < 0 &&    errno != EWOULDBLOCK && errno != EINTR) {    rc1 = errno;}

butex_wait 返回 -1 时检查 errno:

  • EWOULDBLOCK:值不匹配(被 signal/broadcast 改了),这是正常的唤醒路径,不视为错误
  • EINTR:被中断,POSIX 规定条件变量可以虚假唤醒,用户应在循环中检查条件
源码注释对此有详细说明:
// EINTR should not be returned by cond_*wait according to docs on// pthread, however spurious wake-up is OK, just as we do here// so that users can check flags in the loop often companioning// with the cond_wait ASAP.
这意味着 bthread 的 cond_wait 可能因为中断而提前返回——但这是允许的。用户的标准用法是:
bthread_mutex_lock(&mutex);while (!condition) {    bthread_cond_wait(&cond, &mutex);}// 条件满足,执行操作bthread_mutex_unlock(&mutex);
while 循环会处理所有虚假唤醒的情况。

六、timedwait:带超时的等待

intbthread_cond_timedwait(bthread_cond_t* __restrict c,                           bthread_mutex_t* __restrict m,                           const struct timespec* __restrict abstime) {    bthread::CondInternal* ic = reinterpret_cast<bthread::CondInternal*>(c);    const int expected_seq = ic->seq->load(butil::memory_order_relaxed);    if (ic->m.load(butil::memory_order_relaxed) != m) {        bthread_mutex_t* expected_m = NULL;        if (!ic->m.compare_exchange_strong(                expected_m, m, butil::memory_order_relaxed)) {            return EINVAL;        }    }    bthread_mutex_unlock(m);    int rc1 = 0;    if (bthread::butex_wait(ic->seq, expected_seq, abstime) < 0 &&        errno != EWOULDBLOCK && errno != EINTR) {        rc1 = errno;    }    const int rc2 = bthread_mutex_lock_contended(m);    return (rc2 ? rc2 : rc1);}

timedwait 与 wait 的逻辑完全相同,唯一的区别是 butex_wait 的第三个参数从 NULL 变为 abstime——绝对超时时间。

超时后 butex_wait 返回 -1,errno 为 ETIMEDOUT,最终通过 rc1 传递给调用者。

注意 abstime 是绝对时间(CLOCK_REALTIME),与 Linux futex 的相对时间不同。这是 butex 的设计选择(参见本系列第四篇)。


七、wait 的完整流程图

把 wait 的四个步骤与 signal/broadcast 串联起来,形成完整的交互流程:

[消费者] bthread_cond_wait()┌─────────────────────────────────────────────┐│ 1. expected_seq = seq->load()               │  ← 保存当前序列号│ 2. CAS(m, NULL → mutex)                     │  ← 首次绑定时绑定互斥锁│ 3. bthread_mutex_unlock(m)                  │  ← 释放互斥锁│ 4. butex_wait(seq, expected_seq)            │  ← 等待序列号变化│    └─ 挂起当前 bthread/pthread              │└─────────────────────────────────────────────┘                    │                    │ 等待中...                    │[生产者] bthread_cond_signal()              [生产者] bthread_cond_broadcast()┌──────────────────────────┐                ┌──────────────────────────────────────┐│ seq->fetch_add(1)        │                │ seq->fetch_add(1)                    ││ butex_wake(seq)          │                │ butex_requeue(seq, mutex->butex)     ││   └─ 唤醒 1 个等待者     │                │   ├─ 唤醒 1 个等待者                 ││                          │                │   └─ 其余转移到 mutex butex 排队     │└──────────────────────────┘                └──────────────────────────────────────┘                    │                    ▼[消费者] 被唤醒┌─────────────────────────────────────────────┐│ 5. bthread_mutex_lock_contended(m)          │  ← 重新获取互斥锁│    └─ exchange(3) + 可能 butex_wait         ││ 6. 检查条件,如果不满足,回到步骤 1         │└─────────────────────────────────────────────┘
关键观察:wait 操作中,互斥锁的释放和 butex 的等待是两个独立步骤。 在 unlock 和 butex_wait 之间存在一个窗口——signal 可能在这个窗口中发生。但 butex_wait 的语义保证了安全:如果 seq 已经被 signal 改变,butex_wait 发现值不匹配,立即返回 EWOULDBLOCK,不会真正等待。

八、C++ 封装层:ConditionVariable

bthread 提供了 bthread::ConditionVariable 类,是 bthread_cond_t 的 RAII 封装。

8.1 基本接口

class ConditionVariable {public:    ConditionVariable() {        CHECK_EQ(0, bthread_cond_init(&_cond, NULL));    }    ~ConditionVariable() {        CHECK_EQ(0, bthread_cond_destroy(&_cond));    }    voidwait(std::unique_lock<bthread::Mutex>& lock) {        bthread_cond_wait(&_cond, lock.mutex()->native_handler());    }    voidwait(std::unique_lock<bthread_mutex_t>& lock) {        bthread_cond_wait(&_cond, lock.mutex());    }    voidnotify_one()  { bthread_cond_signal(&_cond); }    voidnotify_all()  { bthread_cond_broadcast(&_cond); }    // ...private:    bthread_cond_t _cond;};

构造时初始化,析构时销毁。wait 接受 unique_lock,自动获取底层互斥锁的指针。

8.2 带谓词的 wait

template<typename Predicate>void wait(std::unique_lock<bthread::Mutex>& lock, Predicate p) {    while (!p()) {        bthread_cond_wait(&_cond, lock.mutex()->native_handler());    }}

接受一个谓词 p,内部循环等待直到 p() 返回 true。这消除了手动写 while 循环的需要,也避免了忘记检查条件导致的逻辑错误。

8.3 超时等待

intwait_for(std::unique_lock<bthread::Mutex>& lock, long timeout_us){    return wait_until(lock, butil::microseconds_from_now(timeout_us));}intwait_until(std::unique_lock<bthread::Mutex>& lock, timespec duetime){    const int rc = bthread_cond_timedwait(            &_cond, lock.mutex()->native_handler(), &duetime);    return rc == ETIMEDOUT ? ETIMEDOUT : 0;}
与 std::condition_variable 的一个关键区别:超时时返回 ETIMEDOUT,而非抛出 std::timeout 异常。

8.4 典型用法

bthread::Mutex mutex;bthread::ConditionVariable cond;bool ready = false;// 等待者std::unique_lock<bthread::Mutex> lock(mutex);cond.wait(lock, []{ return ready; });// 等价于:// while (!ready) { cond.wait(lock); }// 通知者{    std::lock_guard<bthread::Mutex> lock(mutex);    ready = true;}cond.notify_one();

九、与 pthread 条件变量的对比

pthread_cond_t
bthread_cond_t
等待者管理
内部维护等待队列
委托给 butex 的等待队列
阻塞方式
futex 系统调用
butex_wait(bthread 让出 CPU,pthread 用 futex)
互斥锁绑定
由调用者保证一致性
首次 wait 时 CAS 绑定,不一致返回 EINVAL
broadcast 优化
glibc 内部实现(FUTEX_REQUEUE)
butex_requeue,直接转移等待者到互斥锁 butex
超时语义
相对时间( abstime 视实现而定)
绝对时间(CLOCK_REALTIME)
混合使用
仅 pthread
同一个条件变量上 bthread/pthread 可互相同步

最后一点尤其重要:bthread_cond_t 天然支持 bthread 和 pthread 的混合同步。这是因为 butex_wait 和 butex_wake 内部根据等待者的 tid 区分类型(参见本系列第四篇),同一个 butex 上可以同时有 bthread 和 pthread 等待者。这意味着一个 pthread 通知者可以唤醒一个等待在条件变量上的 bthread,反之亦然。


十、总结

bthread_cond_t 的设计可以归纳为一句话:用序列号 butex 实现等待/唤醒语义,用 butex_requeue 优化广播性能

四个关键设计:

1. 序列号 butex。seq 是一个 butex(32 位原子整数),初始值为 0。每次 signal/broadcast 递增序列号,等待者通过比较序列号判断是否被唤醒。值不匹配时 butex_wait 立即返回,避免了无效等待。

2. 首次绑定的互斥锁关联。 互斥锁指针 m 在首次 wait 时通过 CAS 绑定。绑定后不可更换——后续使用不同互斥锁的 wait 会返回 EINVAL。这与 POSIX 语义一致。

3. butex_requeue 避免惊群效应。 broadcast 不唤醒所有等待者,而是唤醒第一个,将其余等待者直接转移到互斥锁的 butex 等待队列上。被转移的等待者省去了一次"唤醒 → 重新阻塞"的往返。

4. bthread/pthread 混合同步。 由于底层使用 butex,同一个条件变量上 bthread 和 pthread 可以互相等待和唤醒。这使得在 bthread 代码中使用 pthread 库时,同步原语仍然正确工作。


本文基于 Apache brpc 源码(src/bthread/condition_variable.hsrc/bthread/condition_variable.cppsrc/bthread/types.h)撰写。

最新文章

随机文章

基本 文件 流程 错误 SQL 调试
  1. 请求信息 : 2026-05-05 23:39:43 HTTP/2.0 GET : https://67808.cn/a/486232.html
  2. 运行时间 : 0.087103s [ 吞吐率:11.48req/s ] 内存消耗:4,506.37kb 文件加载:140
  3. 缓存信息 : 0 reads,0 writes
  4. 会话信息 : SESSION_ID=bb28f874b583fd2d41f175f4261e61e7
  1. /yingpanguazai/ssd/ssd1/www/no.67808.cn/public/index.php ( 0.79 KB )
  2. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/autoload.php ( 0.17 KB )
  3. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/composer/autoload_real.php ( 2.49 KB )
  4. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/composer/platform_check.php ( 0.90 KB )
  5. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/composer/ClassLoader.php ( 14.03 KB )
  6. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/composer/autoload_static.php ( 4.90 KB )
  7. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/topthink/think-helper/src/helper.php ( 8.34 KB )
  8. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/topthink/think-validate/src/helper.php ( 2.19 KB )
  9. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/topthink/think-orm/src/helper.php ( 1.47 KB )
  10. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/topthink/think-orm/stubs/load_stubs.php ( 0.16 KB )
  11. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/topthink/framework/src/think/Exception.php ( 1.69 KB )
  12. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/topthink/think-container/src/Facade.php ( 2.71 KB )
  13. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/symfony/deprecation-contracts/function.php ( 0.99 KB )
  14. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/symfony/polyfill-mbstring/bootstrap.php ( 8.26 KB )
  15. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/symfony/polyfill-mbstring/bootstrap80.php ( 9.78 KB )
  16. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/symfony/var-dumper/Resources/functions/dump.php ( 1.49 KB )
  17. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/topthink/think-dumper/src/helper.php ( 0.18 KB )
  18. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/symfony/var-dumper/VarDumper.php ( 4.30 KB )
  19. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/topthink/framework/src/think/App.php ( 15.30 KB )
  20. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/topthink/think-container/src/Container.php ( 15.76 KB )
  21. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/psr/container/src/ContainerInterface.php ( 1.02 KB )
  22. /yingpanguazai/ssd/ssd1/www/no.67808.cn/app/provider.php ( 0.19 KB )
  23. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/topthink/framework/src/think/Http.php ( 6.04 KB )
  24. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/topthink/think-helper/src/helper/Str.php ( 7.29 KB )
  25. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/topthink/framework/src/think/Env.php ( 4.68 KB )
  26. /yingpanguazai/ssd/ssd1/www/no.67808.cn/app/common.php ( 0.03 KB )
  27. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/topthink/framework/src/helper.php ( 18.78 KB )
  28. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/topthink/framework/src/think/Config.php ( 5.54 KB )
  29. /yingpanguazai/ssd/ssd1/www/no.67808.cn/config/app.php ( 0.95 KB )
  30. /yingpanguazai/ssd/ssd1/www/no.67808.cn/config/cache.php ( 0.78 KB )
  31. /yingpanguazai/ssd/ssd1/www/no.67808.cn/config/console.php ( 0.23 KB )
  32. /yingpanguazai/ssd/ssd1/www/no.67808.cn/config/cookie.php ( 0.56 KB )
  33. /yingpanguazai/ssd/ssd1/www/no.67808.cn/config/database.php ( 2.48 KB )
  34. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/topthink/framework/src/think/facade/Env.php ( 1.67 KB )
  35. /yingpanguazai/ssd/ssd1/www/no.67808.cn/config/filesystem.php ( 0.61 KB )
  36. /yingpanguazai/ssd/ssd1/www/no.67808.cn/config/lang.php ( 0.91 KB )
  37. /yingpanguazai/ssd/ssd1/www/no.67808.cn/config/log.php ( 1.35 KB )
  38. /yingpanguazai/ssd/ssd1/www/no.67808.cn/config/middleware.php ( 0.19 KB )
  39. /yingpanguazai/ssd/ssd1/www/no.67808.cn/config/route.php ( 1.89 KB )
  40. /yingpanguazai/ssd/ssd1/www/no.67808.cn/config/session.php ( 0.57 KB )
  41. /yingpanguazai/ssd/ssd1/www/no.67808.cn/config/trace.php ( 0.34 KB )
  42. /yingpanguazai/ssd/ssd1/www/no.67808.cn/config/view.php ( 0.82 KB )
  43. /yingpanguazai/ssd/ssd1/www/no.67808.cn/app/event.php ( 0.25 KB )
  44. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/topthink/framework/src/think/Event.php ( 7.67 KB )
  45. /yingpanguazai/ssd/ssd1/www/no.67808.cn/app/service.php ( 0.13 KB )
  46. /yingpanguazai/ssd/ssd1/www/no.67808.cn/app/AppService.php ( 0.26 KB )
  47. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/topthink/framework/src/think/Service.php ( 1.64 KB )
  48. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/topthink/framework/src/think/Lang.php ( 7.35 KB )
  49. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/topthink/framework/src/lang/zh-cn.php ( 13.70 KB )
  50. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/topthink/framework/src/think/initializer/Error.php ( 3.31 KB )
  51. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/topthink/framework/src/think/initializer/RegisterService.php ( 1.33 KB )
  52. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/services.php ( 0.14 KB )
  53. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/topthink/framework/src/think/service/PaginatorService.php ( 1.52 KB )
  54. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/topthink/framework/src/think/service/ValidateService.php ( 0.99 KB )
  55. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/topthink/framework/src/think/service/ModelService.php ( 2.04 KB )
  56. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/topthink/think-trace/src/Service.php ( 0.77 KB )
  57. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/topthink/framework/src/think/Middleware.php ( 6.72 KB )
  58. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/topthink/framework/src/think/initializer/BootService.php ( 0.77 KB )
  59. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/topthink/think-orm/src/Paginator.php ( 11.86 KB )
  60. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/topthink/think-validate/src/Validate.php ( 63.20 KB )
  61. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/topthink/think-orm/src/Model.php ( 23.55 KB )
  62. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/topthink/think-orm/src/model/concern/Attribute.php ( 21.05 KB )
  63. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/topthink/think-orm/src/model/concern/AutoWriteData.php ( 4.21 KB )
  64. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/topthink/think-orm/src/model/concern/Conversion.php ( 6.44 KB )
  65. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/topthink/think-orm/src/model/concern/DbConnect.php ( 5.16 KB )
  66. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/topthink/think-orm/src/model/concern/ModelEvent.php ( 2.33 KB )
  67. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/topthink/think-orm/src/model/concern/RelationShip.php ( 28.29 KB )
  68. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/topthink/think-helper/src/contract/Arrayable.php ( 0.09 KB )
  69. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/topthink/think-helper/src/contract/Jsonable.php ( 0.13 KB )
  70. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/topthink/think-orm/src/model/contract/Modelable.php ( 0.09 KB )
  71. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/topthink/framework/src/think/Db.php ( 2.88 KB )
  72. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/topthink/think-orm/src/DbManager.php ( 8.52 KB )
  73. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/topthink/framework/src/think/Log.php ( 6.28 KB )
  74. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/topthink/framework/src/think/Manager.php ( 3.92 KB )
  75. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/psr/log/src/LoggerTrait.php ( 2.69 KB )
  76. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/psr/log/src/LoggerInterface.php ( 2.71 KB )
  77. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/topthink/framework/src/think/Cache.php ( 4.92 KB )
  78. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/psr/simple-cache/src/CacheInterface.php ( 4.71 KB )
  79. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/topthink/think-helper/src/helper/Arr.php ( 16.63 KB )
  80. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/topthink/framework/src/think/cache/driver/File.php ( 7.84 KB )
  81. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/topthink/framework/src/think/cache/Driver.php ( 9.03 KB )
  82. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/topthink/framework/src/think/contract/CacheHandlerInterface.php ( 1.99 KB )
  83. /yingpanguazai/ssd/ssd1/www/no.67808.cn/app/Request.php ( 0.09 KB )
  84. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/topthink/framework/src/think/Request.php ( 55.78 KB )
  85. /yingpanguazai/ssd/ssd1/www/no.67808.cn/app/middleware.php ( 0.25 KB )
  86. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/topthink/framework/src/think/Pipeline.php ( 2.61 KB )
  87. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/topthink/think-trace/src/TraceDebug.php ( 3.40 KB )
  88. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/topthink/framework/src/think/middleware/SessionInit.php ( 1.94 KB )
  89. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/topthink/framework/src/think/Session.php ( 1.80 KB )
  90. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/topthink/framework/src/think/session/driver/File.php ( 6.27 KB )
  91. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/topthink/framework/src/think/contract/SessionHandlerInterface.php ( 0.87 KB )
  92. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/topthink/framework/src/think/session/Store.php ( 7.12 KB )
  93. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/topthink/framework/src/think/Route.php ( 23.73 KB )
  94. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/topthink/framework/src/think/route/RuleName.php ( 5.75 KB )
  95. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/topthink/framework/src/think/route/Domain.php ( 2.53 KB )
  96. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/topthink/framework/src/think/route/RuleGroup.php ( 22.43 KB )
  97. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/topthink/framework/src/think/route/Rule.php ( 26.95 KB )
  98. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/topthink/framework/src/think/route/RuleItem.php ( 9.78 KB )
  99. /yingpanguazai/ssd/ssd1/www/no.67808.cn/route/app.php ( 1.72 KB )
  100. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/topthink/framework/src/think/facade/Route.php ( 4.70 KB )
  101. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/topthink/framework/src/think/route/dispatch/Controller.php ( 4.74 KB )
  102. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/topthink/framework/src/think/route/Dispatch.php ( 10.44 KB )
  103. /yingpanguazai/ssd/ssd1/www/no.67808.cn/app/controller/Index.php ( 4.81 KB )
  104. /yingpanguazai/ssd/ssd1/www/no.67808.cn/app/BaseController.php ( 2.05 KB )
  105. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/topthink/think-orm/src/facade/Db.php ( 0.93 KB )
  106. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/topthink/think-orm/src/db/connector/Mysql.php ( 5.44 KB )
  107. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/topthink/think-orm/src/db/PDOConnection.php ( 52.47 KB )
  108. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/topthink/think-orm/src/db/Connection.php ( 8.39 KB )
  109. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/topthink/think-orm/src/db/ConnectionInterface.php ( 4.57 KB )
  110. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/topthink/think-orm/src/db/builder/Mysql.php ( 16.58 KB )
  111. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/topthink/think-orm/src/db/Builder.php ( 24.06 KB )
  112. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/topthink/think-orm/src/db/BaseBuilder.php ( 27.50 KB )
  113. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/topthink/think-orm/src/db/Query.php ( 15.71 KB )
  114. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/topthink/think-orm/src/db/BaseQuery.php ( 45.13 KB )
  115. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/topthink/think-orm/src/db/concern/TimeFieldQuery.php ( 7.43 KB )
  116. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/topthink/think-orm/src/db/concern/AggregateQuery.php ( 3.26 KB )
  117. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/topthink/think-orm/src/db/concern/ModelRelationQuery.php ( 20.07 KB )
  118. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/topthink/think-orm/src/db/concern/ParamsBind.php ( 3.66 KB )
  119. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/topthink/think-orm/src/db/concern/ResultOperation.php ( 7.01 KB )
  120. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/topthink/think-orm/src/db/concern/WhereQuery.php ( 19.37 KB )
  121. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/topthink/think-orm/src/db/concern/JoinAndViewQuery.php ( 7.11 KB )
  122. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/topthink/think-orm/src/db/concern/TableFieldInfo.php ( 2.63 KB )
  123. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/topthink/think-orm/src/db/concern/Transaction.php ( 2.77 KB )
  124. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/topthink/framework/src/think/log/driver/File.php ( 5.96 KB )
  125. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/topthink/framework/src/think/contract/LogHandlerInterface.php ( 0.86 KB )
  126. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/topthink/framework/src/think/log/Channel.php ( 3.89 KB )
  127. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/topthink/framework/src/think/event/LogRecord.php ( 1.02 KB )
  128. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/topthink/think-helper/src/Collection.php ( 16.47 KB )
  129. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/topthink/framework/src/think/facade/View.php ( 1.70 KB )
  130. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/topthink/framework/src/think/View.php ( 4.39 KB )
  131. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/topthink/framework/src/think/Response.php ( 8.81 KB )
  132. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/topthink/framework/src/think/response/View.php ( 3.29 KB )
  133. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/topthink/framework/src/think/Cookie.php ( 6.06 KB )
  134. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/topthink/think-view/src/Think.php ( 8.38 KB )
  135. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/topthink/framework/src/think/contract/TemplateHandlerInterface.php ( 1.60 KB )
  136. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/topthink/think-template/src/Template.php ( 46.61 KB )
  137. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/topthink/think-template/src/template/driver/File.php ( 2.41 KB )
  138. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/topthink/think-template/src/template/contract/DriverInterface.php ( 0.86 KB )
  139. /yingpanguazai/ssd/ssd1/www/no.67808.cn/runtime/temp/6df755f970a38e704c5414acbc6e8bcd.php ( 12.06 KB )
  140. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/topthink/think-trace/src/Html.php ( 4.42 KB )
  1. CONNECT:[ UseTime:0.000585s ] mysql:host=127.0.0.1;port=3306;dbname=no_67808;charset=utf8mb4
  2. SHOW FULL COLUMNS FROM `fenlei` [ RunTime:0.000809s ]
  3. SELECT * FROM `fenlei` WHERE `fid` = 0 [ RunTime:0.000279s ]
  4. SELECT * FROM `fenlei` WHERE `fid` = 63 [ RunTime:0.000268s ]
  5. SHOW FULL COLUMNS FROM `set` [ RunTime:0.000586s ]
  6. SELECT * FROM `set` [ RunTime:0.000195s ]
  7. SHOW FULL COLUMNS FROM `article` [ RunTime:0.000563s ]
  8. SELECT * FROM `article` WHERE `id` = 486232 LIMIT 1 [ RunTime:0.002149s ]
  9. UPDATE `article` SET `lasttime` = 1777995583 WHERE `id` = 486232 [ RunTime:0.002633s ]
  10. SELECT * FROM `fenlei` WHERE `id` = 65 LIMIT 1 [ RunTime:0.000316s ]
  11. SELECT * FROM `article` WHERE `id` < 486232 ORDER BY `id` DESC LIMIT 1 [ RunTime:0.000498s ]
  12. SELECT * FROM `article` WHERE `id` > 486232 ORDER BY `id` ASC LIMIT 1 [ RunTime:0.000495s ]
  13. SELECT * FROM `article` WHERE `id` < 486232 ORDER BY `id` DESC LIMIT 10 [ RunTime:0.001575s ]
  14. SELECT * FROM `article` WHERE `id` < 486232 ORDER BY `id` DESC LIMIT 10,10 [ RunTime:0.002638s ]
  15. SELECT * FROM `article` WHERE `id` < 486232 ORDER BY `id` DESC LIMIT 20,10 [ RunTime:0.003230s ]
0.088779s