iOS中的多线程开发技术第四篇之IO操作

目录

前言

一、多读少写的特性

1.多读:

2.少写:

3.关键需求:

二、常见实现方案

1.dispatch_barrier_async(GCD)

1.dispatch_barrier_async的作用

2.屏障任务的使用场景

1.多读少写

2.任务分组

3.同步操作

3.dispatch_barrier_async 的工作原理

4.示例代码

1.基础用法

2.应用场景:缓存系统

5.注意事项

1.屏障任务对队列的影响

2.性能优化

3.不要滥用屏障任务

2.pthread_rwlock

三、方案对比


前言

        在 iOS 开发中,数据访问的多线程环境非常普遍,例如缓存系统、数据库查询、文件操作等。对于多读少写的场景,设计一个高效且线程安全的方案至关重要。本文将深入介绍多读少写的设计思想,并结合实际代码示例进行详细讲解。

一、多读少写的特性

1.多读

        读取操作频繁,通常是非阻塞的,并且不会修改数据。

2.少写

        写入操作较少,但会对共享资源进行修改,因此需要确保线程安全。

3.关键需求

        读操作应尽可能高效,不影响主线程。

        写操作应保证独占性,防止数据竞争。

二、常见实现方案

1.dispatch_barrier_async(GCD)

        在 iOS 和 macOS 的开发中,dispatch_barrier_async 是 GCD(Grand Central Dispatch)提供的一个强大的 API,用于管理并发任务队列,尤其在多读少写的场景中发挥重要作用。

1.dispatch_barrier_async的作用

        dispatch_barrier_async 的核心作用是在并发队列中插入一个屏障任务(Barrier Task)。这个屏障任务具有以下特点:

  1. 确保屏障任务之前的所有任务完成后才会执行屏障任务。
  2. 确保屏障任务执行时,队列中没有其他任务正在运行。
  3. 屏障任务执行完毕后,队列恢复并发执行模式。

2.屏障任务的使用场景

1.多读少写

        在允许多个线程并发读取的同时,确保写入操作互斥进行。例如:

  1. 缓存系统的读写操作。

  2. 数据库查询和更新。

2.任务分组

        将任务划分为多个阶段,确保一个阶段完成后才开始下一个阶段。

3.同步操作

        在异步任务的执行流中,确保某些关键任务按顺序执行。

这种特性非常适合在多线程场景下,处理多读少写的操作需求。

3.dispatch_barrier_async 的工作原理

        屏障任务只有在自定义并发队列(DISPATCH_QUEUE_CONCURRENT)中才能起到屏障作用。

        在全局并发队列(DISPATCH_QUEUE_GLOBAL)中使用 dispatch_barrier_async,无法实现屏障效果。

        队列执行顺序如下图所示:

[Task 1] -> [Task 2] -> [Barrier Task] -> [Task 3] -> [Task 4]

1. Task 1 和 Task 2 并发执行。

2. Barrier Task 等待 Task 1 和 Task 2 完成后才开始执行。

3. Barrier Task 独占队列,执行时没有其他任务运行。

4. Task 3 和 Task 4 在 Barrier Task 完成后恢复并发执行。

4.示例代码

1.基础用法

        以下示例展示了如何通过 dispatch_barrier_async 实现多读少写的线程安全操作:

#import <Foundation/Foundation.h>

@interface BarrierExample : NSObject
@property (nonatomic, strong) dispatch_queue_t concurrentQueue;
@property (nonatomic, strong) NSMutableArray *dataArray;
@end

@implementation BarrierExample

- (instancetype)init {
    self = [super init];
    if (self) {
        // 创建一个并发队列
        _concurrentQueue = dispatch_queue_create("com.example.concurrentQueue", DISPATCH_QUEUE_CONCURRENT);
        _dataArray = [NSMutableArray array];
    }
    return self;
}

// 读取操作
- (void)readData {
    dispatch_async(self.concurrentQueue, ^{
        NSLog(@"Reading data: %@", self.dataArray);
    });
}

// 写入操作
- (void)writeData:(id)data {
    dispatch_barrier_async(self.concurrentQueue, ^{
        [self.dataArray addObject:data];
        NSLog(@"Writing data: %@", self.dataArray);
    });
}

@end

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        BarrierExample *example = [[BarrierExample alloc] init];
        
        // 模拟多线程读写
        for (int i = 0; i < 5; i++) {
            dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
                [example readData];
            });
        }
        
        dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
            [example writeData:@"New Data"];
        });
        
        // 延时以确保任务完成
        sleep(1);
    }
    return 0;
}

输出示例:

Reading data: ()
Reading data: ()
Writing data: ("New Data")
Reading data: ("New Data")
Reading data: ("New Data")

        读操作并发执行,效率高。

        写操作确保独占性,数据安全。

2.应用场景:缓存系统

        在缓存系统中,读取数据可以并发进行,但写入需要独占队列,确保数据一致性:

- (id)readFromCache {
    __block id result = nil;
    dispatch_sync(self.concurrentQueue, ^{
        result = [self.cache objectForKey:@"key"];
    });
    return result;
}

- (void)writeToCache:(id)value {
    dispatch_barrier_async(self.concurrentQueue, ^{
        [self.cache setObject:value forKey:@"key"];
    });
}

5.注意事项

1.屏障任务对队列的影响

        只能在自定义并发队列中起作用。

        在全局并发队列中不会阻塞其他任务。

2.性能优化

        读操作不会阻塞,性能高。

        写操作独占队列,适合写入频率较低的场景。

3.不要滥用屏障任务

        如果数据写入频繁,屏障任务可能会成为性能瓶颈。

        对于写入频繁的场景,考虑其他机制(如 pthread_rwlock 或 NSLock)。

2.pthread_rwlock

        pthread_rwlock 是 POSIX 提供的读写锁,专为多读少写场景设计。

        它提供两种操作:

  1. pthread_rwlock_rdlock:加读锁,允许多个线程同时读取。
  2. pthread_rwlock_wrlock:加写锁,独占资源。

示例代码

#import <Foundation/Foundation.h>
#import <pthread.h>

@interface RWLockExample : NSObject
@property (nonatomic, strong) NSMutableArray *dataArray;
@property (nonatomic) pthread_rwlock_t rwLock;
@end

@implementation RWLockExample

- (instancetype)init {
    self = [super init];
    if (self) {
        _dataArray = [NSMutableArray array];
        pthread_rwlock_init(&_rwLock, NULL); // 初始化读写锁
    }
    return self;
}

- (void)dealloc {
    pthread_rwlock_destroy(&_rwLock); // 销毁读写锁
}

// 读取操作
- (void)readData {
    pthread_rwlock_rdlock(&_rwLock); // 加读锁
    NSLog(@"Reading data: %@", self.dataArray);
    pthread_rwlock_unlock(&_rwLock); // 解锁
}

// 写入操作
- (void)writeData:(id)data {
    pthread_rwlock_wrlock(&_rwLock); // 加写锁
    [self.dataArray addObject:data];
    NSLog(@"Writing data: %@", self.dataArray);
    pthread_rwlock_unlock(&_rwLock); // 解锁
}

@end

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        RWLockExample *example = [[RWLockExample alloc] init];
        
        // 模拟多线程读写
        for (int i = 0; i < 5; i++) {
            dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
                [example readData];
            });
        }
        
        dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
            [example writeData:@"New Data"];
        });
        
        sleep(1);
    }
    return 0;
}

1.优势

        高度灵活,可精确控制读写锁的加锁和解锁。

        性能优秀,尤其适合高并发读操作。

2.缺点

        代码复杂度较高。

        手动管理锁容易导致死锁或解锁失败。

三、方案对比

特性

dispatch_barrier_async

pthread_rwlock

实现复杂度

性能

适中

较高

适用场景

数据量较小,非实时要求场景

数据量较大,高实时性要求场景

灵活性

较低

推荐程度

四、总结

        在 iOS 多线程开发中,针对多读少写场景,应优先选择性能和实现复杂度平衡的方案。

        如果项目简单且代码维护成本较低,推荐使用 GCD 的 dispatch_barrier_async。

       如果需要更高的性能,尤其是在高并发场景下,建议使用 pthread_rwlock 进行精细化控制。

掌握这些方案,可以帮助开发者在多线程环境下有效地管理共享资源,提升应用性能和用户体验。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

我叫柱子哥

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值