线程与任务

llbc 的线程层提供三组能力:以 LLBC_Task 为核心的任务队列模型、由 LLBC_ThreadMgr 管理的底层线程句柄、以及一套锁/守卫/TLS 工具。绝大多数业务代码只需继承 LLBC_Task、通过 Push/Pop 收发消息;只有在需要精细控制线程生命周期时才直接使用 LLBC_ThreadMgr。

LLBC_Task 任务队列模型

LLBC_Task:任务模型

LLBC_Task 是 llbc 的线程任务基类。调用 Activate 启动若干工作线程,每条线程独立运行 Svc();所有线程退出后框架调一次 Cleanup() 进行收尾。派生类必须实现这两个纯虚方法。

class MyTask final : public LLBC_Task
{
public:
    void Svc() override
    {
        LLBC_MessageBlock *block = nullptr;
        while (IsActivated())
        {
            TimedPop(block, 200);  // 超时 200 ms,防止 Svc 永久阻塞
            if (!block) continue;
            // 处理 block ...
            delete block;
        }
    }
    void Cleanup() override { /* 资源清理 */ }
};

MyTask task;
task.Activate(4);  // 启动 4 条工作线程,默认 Normal 优先级
// 向任务推送消息(见下节)
task.Wait();       // 等待所有线程结束(Svc 全部返回后触发 Cleanup)

GetTaskState() 返回 LLBC_TaskState 枚举值(NotActivated / Activating / Activated / Deactivating),可用 LLBC_TaskState::GetDesc(state) 转为可读字符串。

Activate 与 Wait 不可重入:同一 LLBC_Task 对象同一时刻只能处于一次激活周期。Svc() 内部用 IsActivated() 判断任务是否仍处于 Activated 状态来控制退出逻辑。

LLBC_MessageBlock 与消息收发

LLBC_MessageBlock 是消息队列的最小传输单元,封装一段连续缓冲区,读写游标独立。

// 生产方:序列化数据,写入 MessageBlock,推入 Task
LLBC_Stream stream;
stream.Write(LLBC_String().format("msg #%d", i));
LLBC_MessageBlock *block = new LLBC_MessageBlock;
block->Write(stream.GetBuf(), stream.GetWritePos());
task.Push(block);   // 线程安全,内部走 LLBC_MessageQueue

// 消费方(在 Svc() 内):
LLBC_MessageBlock *blk = nullptr;
task.Pop(blk);                                     // 阻塞直到有消息
LLBC_Stream in(blk->GetData(), blk->GetReadableSize());
LLBC_String msg; in.Read(msg);
delete blk;

Task 还提供 TryPop(非阻塞)和 TimedPop(block, ms)(带超时),以及 PopAll 一次取出全部块(结果以单向链表形式通过 GetNext() 遍历)。GetMessageSize() 返回队列中待处理块数量。

LLBC_MessageQueue 独立使用

LLBC_MessageQueue 可脱离 LLBC_Task 单独使用,支持双端插入与弹出。

LLBC_MessageQueue q;

// 生产(任意线程)
LLBC_MessageBlock *blk = new LLBC_MessageBlock;
blk->Write("hello", 5);
q.PushBack(blk);          // 也可 PushFront

// 消费(阻塞等待)
LLBC_MessageBlock *out = nullptr;
q.PopFront(out);          // 阻塞直到有数据

// 非阻塞尝试:返回 true 表示取到数据
if (q.TryPopFront(out)) { /* 有数据 */ delete out; }

// 超时 200 ms
if (q.TimedPopFront(out, 200)) { /* 有数据 */ delete out; }

PopAll 通过出参传出链表头指针(返回值是 bool/int 表示是否成功),调用方需循环 GetNext() 并逐一 delete 节点,不要整体释放。

LLBC_ThreadMgr:底层线程管理

LLBC_ThreadMgr 以单例形式暴露,通过宏 LLBC_ThreadMgrSingleton 访问。通常由 LLBC_Task 内部调用,业务代码也可直接创建原始线程。

// 创建 3 条线程,同属一个 group handle
LLBC_Handle grp = LLBC_ThreadMgrSingleton->CreateThreads(
    3,
    [](void *) { /* 线程体 */ },
    nullptr,                        // arg
    LLBC_ThreadPriority::Normal
);

// 等待整组线程结束
LLBC_ThreadMgrSingleton->WaitGroup(grp);

// 静态工具方法(任意线程均可调用)
LLBC_ThreadId  tid    = LLBC_ThreadMgr::CurThreadId();
bool           inLlbc = LLBC_ThreadMgr::InLLBCThread();
LLBC_Handle    grpH   = LLBC_ThreadMgr::CurGroupHandle();

线程启动/停止钩子:

LLBC_ThreadMgrSingleton->SetThreadStartHook(
    [](LLBC_Handle h) { /* 线程刚启动时回调 */ });
LLBC_ThreadMgrSingleton->SetThreadStopHook(
    [](LLBC_Handle h) { /* 线程即将退出时回调 */ });

Cancel/CancelGroup/CancelAll 依赖平台线程强制取消语义(POSIX pthread_cancel / Win32 TerminateThread),可能导致资源泄漏。推荐通过向消息队列推送哨兵消息,让 Svc() 主动退出,而非强制取消。

锁类型一览

所有互斥锁均继承 LLBC_ILock 接口(Lock() / TryLock() / Unlock()),可与 LLBC_LockGuard 搭配使用。

类型 底层实现 适用场景
LLBC_SimpleLock 系统互斥量 (mutex) 通用互斥,等待时让出 CPU
LLBC_RecursiveLock 递归互斥量 同一线程可重入加锁的场景
LLBC_FastLock x86 汇编 CAS 或系统 mutex 极短临界区,x86/x64 架构最优
LLBC_SpinLock 自旋锁 临界区极短、不希望发生线程调度的场景
LLBC_DummyLock 空实现(无操作) 模板参数占位,单线程场景

LLBC_RWLock 提供独立的读写接口(不继承 LLBC_ILock):

LLBC_RWLock rwLock;

// 读者(允许并发)
rwLock.ReadLock();
// ... 读共享数据 ...
rwLock.ReadUnlock();

// 写者(独占)
rwLock.WriteLock();
// ... 修改共享数据 ...
rwLock.WriteUnlock();

// 非阻塞变体:bool ReadTryLock() / bool WriteTryLock()

LLBC_LockGuard 与各类守卫

LLBC_LockGuard 以 RAII 方式管理任意 LLBC_ILock 的加解锁,防止异常或提前 return 时忘记解锁。

LLBC_SimpleLock lock;

{
    LLBC_LockGuard guard(lock);   // 构造时 Lock()
    // 临界区操作
}                                 // 析构时 Unlock()

// reverse=true:构造时 Unlock(),析构时 Lock()(罕用,适合临时解锁)
LLBC_LockGuard reverseGuard(lock, /*reverse=*/true);

其余守卫类用于生命周期管理,均在析构时自动执行对应操作:

SomeClass *p = new SomeClass;
LLBC_DeleteGuard<SomeClass> dg(p);   // 析构时 delete p,并将 p 置 nullptr

void *mem = malloc(64);
LLBC_FreeGuard<void> fg(mem);        // 析构时 free(mem)

// 离开作用域时调用任意可调用对象
LLBC_InvokeGuard ig([]() { LLBC_PrintLn("cleanup triggered"); });

// 绑定成员函数:
LLBC_InvokeGuard ig2(obj, &MyClass::Cleanup, arg);

LLBC_Tls:线程局部存储

LLBC_Tls<T> 是平台无关的 TLS 封装,每个线程持有独立的 T*,通过 operator* 和 operator-> 透明访问。

static LLBC_Tls<int> g_tls;   // 全局/静态声明

// 在每条线程中(通常在线程入口或首次使用前):
g_tls.SetValue(new int(0));    // 分配本线程专属存储(旧值会被自动 delete)
(*g_tls) += 1;                 // 解引用访问
LLBC_PrintLn("tls = %d", *g_tls);
g_tls.ClearValue();            // 清除并 delete 当前线程的值

SetValue 的第二个参数 valueClearMeth 控制旧值的释放方式:LLBC_TlsValueClearMeth::Delete(默认,用 delete)或 LLBC_TlsValueClearMeth::Free(用 free)。若 TLS 持有的指针由 malloc 分配,必须显式传入 Free,否则行为未定义。LLBC_Tls 对象本身不管理跨线程的析构——线程结束时若未调 ClearValue(),该线程分配的存储将泄漏。

参照

头文件:

真实测试示例:

下一步