多线程常见实例

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);
            }
    });
}
  1. dispatch_group:等待一系列任务(block)执行完成后,再做一些收尾的工作;
  2. dispatch_barrier_async/dispatch_barrier_sync:当目前正在执行的block运行完成后,阻塞这个block后面添加的block,执行队列插入的block直到其完成,再继续后续的任务;
  3. 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();  
    }  
}

用上叙述的互斥锁来模拟一个读写锁,实现步骤如下:

  1. ReadLock 和 WriteLock 都加上一个写锁,以保证与其他写操作互斥;
  2. 整个读写锁是允许重复读的,需添加一个共享变量readCound,用以计读线程数;
  3. 操作 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();  
        }  
    }  
      
}

你可能感兴趣的:(多线程常见实例)