1. GCD的cancel操作
NSOperation 是基于GCD来实现的,其中的任务可以设置依赖、暂停、取消,都是基于 dispatch_block_t 的具体功能进行的:
//// cancel动作
dispatch_block_t block = dispatch_block_creat(0, ^{
// 执行任务
});
dispatch_block_cancel(block);
2. 线程安全的经典案例
下面一段代码功能很简单:循环1000次,每次异步处理一个“实例化并赋值”的操作。这会有什么问题吗?
for (int i=0; i<1000; i++) {
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRORITY_DEFAULT, 0), ^{
self.person = [[Person alloc] init];
});
}
这段代码有可能引起崩溃。原因在于多线程为 self.person 赋值这一操作,多个被实例化出来的对象并行赋值给self.person,导致原来self.person指向的实例对象被释放多次。
3. 多线程处理图片(多线程,并限制数量)
多线程处理图片,每次最多4个线程同时执行,所有图片处理线程执行完之后再执行回调:
- (UIImage *)processImage:(UIImage *)image {
// 处理过程
signal(semaphore);
}
/// 图片处理API
/// 1. 并发处理 2. 并发数最多4个 3. 正确回调
- (void)processImages:(NSArray *)images completion:(void (^)(NSArray * precessedImages))completion {
dispatch_group_t group //创建一个group
dispatch_queue_t queue //创建一个queue
semaphore = 4 //创建一个信号量
__block NSMutableArray *imgArr = [NSMutableArray array];
for(UIImage *image in images) {
dispatch_group_async(group, queue, ^(){
wait(semaphore);
UIImage *img =[self processImage:image];
[imgArr addObject:img];
});
}
dispatch_group_notify(group, queue, ^(){
if(completion) {
completion(imgArr);
}
});
}
- dispatch_group:等待一系列任务(block)执行完成后,再做一些收尾的工作;
- dispatch_barrier_async/dispatch_barrier_sync:当目前正在执行的block运行完成后,阻塞这个block后面添加的block,执行队列插入的block直到其完成,再继续后续的任务;
- dispatch_barrier只在自定义的并发队列上有效,在全局(Global)并发队列、串行队列上,效果跟dispatch_(a)sync效果一样,因此,要小心死锁的发生。
4. 关于读写锁
- 使用 pthread_rwlock 来实现:
#import "ViewController.h"
#import
@interface ViewController ()
@property (assign, nonatomic) pthread_rwlock_t lock;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
// 初始化锁
pthread_rwlock_init(&_lock, NULL);
dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
for (int i = 0; i < 10; i++) {
dispatch_async(queue, ^{
[self read];
});
dispatch_async(queue, ^{
[self write];
});
}
}
- (void)read {
//读锁
pthread_rwlock_rdlock(&_lock);
sleep(1);
NSLog(@"%s", __func__);
pthread_rwlock_unlock(&_lock);
}
- (void)write {
//写锁
pthread_rwlock_wrlock(&_lock);
sleep(1);
NSLog(@"%s", __func__);
pthread_rwlock_unlock(&_lock);
}
// pthread_rwlock_destroy 是pthread锁,要销毁
- (void)dealloc {
pthread_rwlock_destroy(&_lock);
}
@end
- 用 同步任务+栅栏 来实现:
// 读数据(一般同步的读,异步可改用block回调)
- (id)objectForKey:(NSString *)key {
__block id obj;
// 同步读取指定数据:
dispatch_sync(conQueue, ^{
obj = [self.dataDict objectForKey:key];
});
return obj;
}
// 写数据
- (void)setObject:(id)obj forKey:(NSString *)key {
// 异步栅栏调用设置数据:
dispatch_barrier_async(conQueue, ^{
[self.dataDict setObject:obj forKey:key];
});
}
- 用自信号量来实现:
信号量最主要的是P操作和V操作,
P:semaphore减1,若其小于等于0,当前线程被阻塞;
V:semaphore加1,若其大于0,则唤醒一个等待线程。
利用信号量实现一个互斥锁。互斥锁能够保证在某一个时刻,只有一个线程可以进入临界区。
public class MutexLock {
// 互斥信号量为1
private Semaphore mutex = new Semaphore(1);
public void lock() throws InterruptedException {
mutex.acquire();
}
public void unlock() {
mutex.release();
}
}
用上叙述的互斥锁来模拟一个读写锁,实现步骤如下:
- ReadLock 和 WriteLock 都加上一个写锁,以保证与其他写操作互斥;
- 整个读写锁是允许重复读的,需添加一个共享变量readCound,用以计读线程数;
- 操作 readCound 时,需要互斥,用 MutexLock 保护;
在ReadLock和WriteLock都加上一个写锁。这样保证读操作还是写操作同时只有一个线程可以进行。
读写锁,是可以允许重复读的。所以添加一个readCound计数。表示当前有多少读线程。因为readCount是共享变量。所以用countMutex进行保护。
当readCount等于0时,表示第一个读线程。尝试获取锁。如果拿到写锁,readCount++。下一个读线程就不用获取锁。如果没有获取锁,则readCount一直是0。读线程处于等待状态。
离开时,只有所有的读结束,才释放锁。唤醒一个等待线程。一般是写线程。
具体实现
public class ReadWriteLock {
private int readCount = 0;
private MutexLock countMutex = new MutexLock();
private MutexLock writeMutex = new MutexLock();
public class ReadLock{
public void lock() throws InterruptedException{
//readCount是共享变量,所以需要实现一个锁来控制读写
//synchronized(ReadWriteLock.class){} countMutex.lock();
//只有是第一个读者,才将写锁加锁。其他的读者都是进行下一步
if(readCount == 0){
writeMutex.lock();
}
++readCount;
countMutex.unlock();
}
public void unlock() throws InterruptedException{
countMutex.lock();
readCount--;
//只有当读者都读完了,才会进行写操作
if(readCount == 0){
writeMutex.unlock();
}
countMutex.unlock();
}
}
public class WriteLock{
public void lock() throws InterruptedException{
writeMutex.lock();
}
public void unlock(){
writeMutex.unlock();
}
}
}
测试代码
public class Main {
private static ReadWriteLock readWriteLock = new ReadWriteLock();
private static ReadWriteLock.ReadLock readLock = readWriteLock.new ReadLock();
private static ReadWriteLock.WriteLock writeLock = readWriteLock.new WriteLock();
public static void main(String[] args){
test();
}
private static void test(){
Thread t;
int writeNum = (int)(Math.random() * 10);
for(int i = 0; i < 10; i++){
// if(i == writeNum){
if((int)(Math.random() * 10) > 5){
t = new Thread(){
public void run(){
try{
writeLock.lock();
System.out.println(this.getName() + " writing");
Thread.sleep(
(int)(Math.random() * 6 * 1000));
System.out.println(this.getName() + " write done");
writeLock.unlock();
}catch (Exception e){}
}
};
}else{
t = new Thread(){
public void run(){
try{
readLock.lock();
System.out.println(this.getName() + " reading");
Thread.sleep(
(int)(Math.random() * 3 * 1000));
System.out.println(this.getName() + " read done");
readLock.unlock();
}catch (Exception e){}
}
};
}
t.setName("thread " + i);
t.start();
}
}
}