iOS - 线程与AutoreleasePoolPage

1. AutoreleasePoolPage 结构

objectivec 复制代码
struct AutoreleasePoolPage {
    static pthread_key_t key;      // 只占用一份内存空间
    
    magic_t const magic;
    id *next;
    pthread_t const thread;
    AutoreleasePoolPage *parent;
    AutoreleasePoolPage *child;
    // ...其他成员变量
}

pthread 的 TLS (Thread Local Storage) 机制通过 key-value 映射表实现了这个功能。

2. 基本原理

每个线程都维护着自己的存储空间(类似哈希表),所有线程共享同一个 key,但每个线程的 value 是独立的:

objectivec 复制代码
// pthread 内部实现的简化版本
struct pthread_internal_t {
    // 每个线程都有自己的 specific 数组
    void *specific[PTHREAD_KEYS_MAX];  // 线程特定数据数组
};

// 所有线程共享的键值数组
struct pthread_key_struct {
    bool in_use;                      // 该 key 是否在使用
    void (*destructor)(void *value);  // 析构函数
} pthread_keys[PTHREAD_KEYS_MAX];

3. 关键操作

3.1 创建 key

objectivec 复制代码
// 在 tls_init 中创建 key
void tls_init(void) {
    // pthread_key_create 会在 pthread_keys 数组中分配一个槽位
    AutoreleasePoolPage::key = pthread_key_create(&autoreleasePoolDealloc);
}

3.2 设置线程特定数据

objectivec 复制代码
// 为当前线程设置 page
static void setHotPage(AutoreleasePoolPage *page) {
    // 将 page 存储到当前线程的 specific 数组中
    pthread_setspecific(AutoreleasePoolPage::key, page);
}

3.3 获取线程特定数据

objectivec 复制代码
static inline AutoreleasePoolPage *hotPage() {
    // 从当前线程的 specific 数组中获取 page
    AutoreleasePoolPage *result = (AutoreleasePoolPage *)
        pthread_getspecific(AutoreleasePoolPage::key);
    return result;
}

4. 工作流程示意

objectivec 复制代码
// 简化的内部实现原理
pthread_key_create(key, destructor) {
    // 分配一个全局唯一的 key
    *key = allocate_key_index();
    // 注册析构函数
    pthread_keys[*key].destructor = destructor;
}

pthread_setspecific(key, value) {
    // 获取当前线程
    pthread_internal_t *thread = pthread_self();
    // 将值存储在当前线程的特定位置
    thread->specific[key] = value;
}

pthread_getspecific(key) {
    // 获取当前线程
    pthread_internal_t *thread = pthread_self();
    // 返回当前线程存储的值
    return thread->specific[key];
}

5. 实际应用示例

objectivec 复制代码
// 线程 1
void *thread1_func(void *arg) {
    @autoreleasepool {
        // pthread_getspecific(key) 返回线程1的 page
        AutoreleasePoolPage *page = hotPage();
        // page->thread == thread1
    }
    return NULL;
}

// 线程 2
void *thread2_func(void *arg) {
    @autoreleasepool {
        // pthread_getspecific(key) 返回线程2的 page
        AutoreleasePoolPage *page = hotPage();
        // page->thread == thread2
    }
    return NULL;
}

6. 关键点总结

  1. key 的作用
  • 作为索引,用于在线程的特定数据数组中定位数据
  • 全局唯一,所有线程共享
  1. 线程隔离
  • 每个线程维护独立的存储空间
  • 相同的 key 在不同线程中映射到不同的值
  1. 内存管理
  • 线程退出时自动调用析构函数
  • 自动清理线程特定数据
  1. 性能考虑
  • 快速访问:O(1) 的时间复杂度
  • 空间效率:每个线程只存储使用的数据

这就是为什么使用同一个 key 可以在不同线程中获取不同的 page 的原理。

相关推荐
SameX2 小时前
iOS 独立开发者转鸿蒙:我把足迹地图 App 迁移到 ArkTS 的全过程
ios
sakiko_17 小时前
Swift学习笔记42-SwiftUI的属性包装器(讲解+面试)
笔记·学习·ios·swiftui·swift
Patrick_Wilson1 天前
iOS Safari 按钮点击无响应问题修复指南
前端·ios·safari
随遇丿而安1 天前
第 3 周:UIButton 点了没反应,因为它默认就没有按压态
ios
思盛iOS签名上架2 天前
App苹果签名有什么作用?
macos·ios·cocoa
CocoaKier2 天前
Facebook广告后台收不到Adjust回传数据,我们排查了一个月终于找到了根因
ios·facebook
FairGuard手游加固3 天前
iOS 游戏加固技术解析:代码混淆、资源加密、内存保护与重签名检测
安全·游戏·macos·unity·ios·objective-c
思盛iOS签名上架3 天前
iOS企业签名原理是什么?
macos·ios·cocoa
Daniel_Coder3 天前
我用 SwiftUI + GRDB 做了一个「按时吃药·服药提醒」App
ios·swiftui·swift·widgetkit