0
点赞
收藏
分享

微信扫一扫

多线程常见实例

夏侯居坤叶叔尘 2021-09-29 阅读 60
日记本

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<UIImage *> *)images completion:(void (^)(NSArray<UIImage *> * 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);
}
});
}

4. 关于读写锁

  • 使用 pthread_rwlock 来实现:
#import "ViewController.h"
#import <pthread.h>

@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(

sleep(1);
NSLog(@"%s", __func__);

pthread_rwlock_unlock(
}

- (void)write {
//写锁
pthread_rwlock_wrlock(

sleep(1);
NSLog(@"%s", __func__);

pthread_rwlock_unlock(
}

// pthread_rwlock_destroy 是pthread锁,要销毁
- (void)dealloc {
pthread_rwlock_destroy(
}

@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];
});
}
  • 用自信号量来实现:

利用信号量实现一个互斥锁。互斥锁能够保证在某一个时刻,只有一个线程可以进入临界区。

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计数。表示当前有多少读线程。因为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();
}
}

}
举报

相关推荐

0 条评论