美文网首页
GCD线程同步

GCD线程同步

作者: xxxxxxxxx_ios | 来源:发表于2018-12-25 19:44 被阅读0次

引导:多线程安全隐患?
一块资源可能会被多个线程共享,也就是多个线程可能会访问同一块资源,比如多个线程访问同一个对象、同一个变量、同一个文件,当多个线程访问同一块资源时,很容易引发数据错乱和数据安全问题。

1.多线程安全隐患分析
多个线程访问同一个变量
2.多线程安全隐患解决方案

使用线程同步技术(同步,就是协同步调,按预定的先后次序进行),常见的线程同步技术是:加锁

加锁
3.iOS的线程同步方案
    OSSpinLock
    os_unfair_lock
    pthread_mutex
    dispatch_semaphore
    dispatch_queue(DISPATCH_QUEUE_SERIAL)
    NSLock
    NSRecursiveLock
    NSCondition
    NSConditionLock
    @synchronized

下面将对上述所有同步方案一一介绍。

1.OSSpinLock
  • 介绍:
    1.OSSpinLock叫做”自旋锁”,等待锁的线程会处于忙等(busy-wait)状态,一直占用着CPU资源;
    2.目前已经不再安全,可能会出现优先级反转问题;
    3.如果等待锁的线程优先级较高,它会一直占用着CPU资源,优先级低的线程就无法释放锁;
    4.需要导入头文件#import <libkern/OSAtomic.h>
  • 代码
#import "ViewController.h"
#import <libkern/OSAtomic.h>

@interface ViewController ()
@property (nonatomic, assign) NSInteger allTicketCount;
@property (nonatomic, assign) OSSpinLock lock;
@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];

    self.allTicketCount = 10;
    self.lock = OS_SPINLOCK_INIT;
    
    // 开启两条线程 各卖五张票
    dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
    dispatch_async(queue, ^{
        for (NSInteger i = 0; i < 5; i ++) {
            [self sellTicket];
        }
    });
    dispatch_async(queue, ^{
        for (NSInteger i = 0; i < 5; i ++) {
            [self sellTicket];
        }
    });
    
}

// 卖票
- (void)sellTicket {
    
    // 加锁
    OSSpinLockLock(&_lock);
    
    sleep(1);
    self.allTicketCount --;
    NSLog(@"还有%ld张票 -- %@",self.allTicketCount,[NSThread currentThread]);
    
    // 解锁
    OSSpinLockUnlock(&_lock);
}

@end

  • 运行结果
结果
2.os_unfair_lock
  • 介绍:
    1.os_unfair_lock用于取代不安全的OSSpinLock ,从iOS10开始才支持;
    2.从底层调用看,等待os_unfair_lock锁的线程会处于休眠状态,并非忙等;
    3.需要导入头文件#import <os/lock.h>
  • 代码
#import "ViewController.h"
#import <os/lock.h>
@interface ViewController ()
@property (nonatomic, assign) NSInteger allTicketCount;
@property (nonatomic, assign) os_unfair_lock lock;
@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];

    self.allTicketCount = 10;
    self.lock = OS_UNFAIR_LOCK_INIT;;
    
    // 开启两条线程 各卖五张票
    dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
    dispatch_async(queue, ^{
        for (NSInteger i = 0; i < 5; i ++) {
            [self sellTicket];
        }
    });
    dispatch_async(queue, ^{
        for (NSInteger i = 0; i < 5; i ++) {
            [self sellTicket];
        }
    });
    
}

// 卖票
- (void)sellTicket {
    
    // 加锁
    os_unfair_lock_lock(&_lock);
    
    sleep(1);
    self.allTicketCount --;
    NSLog(@"还有%ld张票 -- %@",self.allTicketCount,[NSThread currentThread]);
    
    // 解锁
    os_unfair_lock_unlock(&_lock);
}

@end
  • 运行结果


    结果
3.pthread_mutex
  • 介绍:
    1.mutex叫做”互斥锁”,等待锁的线程会处于休眠状态
    2.需要导入头文件#import <pthread.h>
  • 代码
#import "ViewController.h"
#import <pthread.h>
@interface ViewController ()
@property (nonatomic, assign) NSInteger allTicketCount;
@property (assign, nonatomic) pthread_mutex_t mutex;
@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];

    self.allTicketCount = 10;
    pthread_mutex_init(&_mutex, NULL);
    
    // 开启两条线程 各卖五张票
    dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
    dispatch_async(queue, ^{
        for (NSInteger i = 0; i < 5; i ++) {
            [self sellTicket];
        }
    });
    dispatch_async(queue, ^{
        for (NSInteger i = 0; i < 5; i ++) {
            [self sellTicket];
        }
    });
    
}

// 卖票
- (void)sellTicket {
    
    // 加锁
    pthread_mutex_lock(&_mutex);
    
    sleep(1);
    self.allTicketCount --;
    NSLog(@"还有%ld张票 -- %@",self.allTicketCount,[NSThread currentThread]);
    
    // 解锁
    pthread_mutex_unlock(&_mutex);
}

- (void)dealloc
{
    pthread_mutex_destroy(&_mutex);
}
@end
  • 运行结果


    结果
4.pthread_mutex 递归锁
  • 介绍
    递归锁:允许同一个线程对一把锁进行重复加锁
  • 代码
#import "ViewController.h"
#import <pthread.h>
@interface ViewController ()
@property (nonatomic, assign) NSInteger allTicketCount;
@property (assign, nonatomic) pthread_mutex_t mutex;
@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];

    self.allTicketCount = 10;
    
    // 初始化属性
    pthread_mutexattr_t attr;
    pthread_mutexattr_init(&attr);
    pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
    // 初始化锁
    pthread_mutex_init(&_mutex, &attr);
    // 销毁属性
    pthread_mutexattr_destroy(&attr);
    
    // 调用递归
    [self otherTest];
}


- (void)otherTest
{
    
    pthread_mutex_lock(&_mutex);
    
    NSLog(@"%s -- %@", __func__,[NSThread currentThread]);
    
    static int count = 0;
    if (count < 10) {
        count++;
        // 一般情况下上锁之后,执行到此处会卡住变成死锁,因为接下来的代码试图上锁,但第一次上的锁还没有解锁 所以产生死锁,使用递归锁之后,代码能递归执行。
        [self otherTest];
    }
    
    pthread_mutex_unlock(&_mutex);
}

- (void)dealloc
{
    pthread_mutex_destroy(&_mutex);
}
@end
  • 运行结果


    结果
5.pthread_mutex 条件锁
  • 介绍
    操作背景:开启两个线程,一个往数组中添加元素,一个从数组中删除元素,但删除元素的操作应该在数组中有元素的前提下进行。
  • 代码
#import "ViewController.h"
#import <pthread.h>
@interface ViewController ()
@property (assign, nonatomic) pthread_mutex_t mutex;
@property (assign, nonatomic) pthread_cond_t cond;
@property (strong, nonatomic) NSMutableArray *data;
@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];

    self.data = [NSMutableArray array];
    // 初始化锁
    pthread_mutex_init(&_mutex, NULL);
    // 初始化条件
    pthread_cond_init(&_cond, NULL);
    
    // 两个线程同时操作数据
    [self otherTest];
}


- (void)otherTest
{
    dispatch_queue_t queue = dispatch_queue_create("queue", DISPATCH_QUEUE_CONCURRENT);
    dispatch_async(queue, ^{
        for (NSInteger i = 0; i < 5; i ++) {
            [self add];
        }
    });
    
    dispatch_async(queue, ^{
        for (NSInteger i = 0; i < 5; i ++) {
            [self remove];
        }
    });
}

- (void)add {
    
    pthread_mutex_lock(&_mutex);
    
    sleep(1);
    
    [self.data addObject:@"test"];
    
    NSLog(@"add - %@",[NSThread currentThread]);
    
    // 对等待这个条件的线程发送信号 可以执行了
    pthread_cond_signal(&_cond);
    
//    // 对等待所有条件的线程发送信号 可以执行了
//    pthread_cond_broadcast(&_cond);
    
    pthread_mutex_unlock(&_mutex);
}

- (void)remove {
    
    pthread_mutex_lock(&_mutex);
    
    // 如果data.count为0,就进入等待状态
    if (self.data.count == 0) {
        pthread_cond_wait(&_cond, &_mutex);
    }
    
    sleep(1);
    
    [self.data removeLastObject];
    
    NSLog(@"remove- %@",[NSThread currentThread]);
    
    pthread_mutex_unlock(&_mutex);
}

- (void)dealloc
{
    pthread_mutex_destroy(&_mutex);
    pthread_cond_destroy(&_cond);
}
@end
  • 运行结果


    结果
6.NSLock
  • 介绍
    pthread_mutex普通锁的封装
  • 代码
#import "ViewController.h"
@interface ViewController ()
@property (nonatomic, assign) NSInteger allTicketCount;
@property (nonatomic, strong) NSLock *lock;
@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    
    self.lock = [[NSLock alloc] init];
    self.allTicketCount = 10;

    // 两个线程同时操作数据
    dispatch_queue_t queue = dispatch_queue_create("queue", DISPATCH_QUEUE_CONCURRENT);
    dispatch_async(queue, ^{
        for (NSInteger i = 0; i < 5; i ++) {
            [self sellTicket];
        }
    });
    
    dispatch_async(queue, ^{
        for (NSInteger i = 0; i < 5; i ++) {
            [self sellTicket];
        }
    });
}


// 卖票
- (void)sellTicket {
    
    // 加锁
    [self.lock lock];
    
    sleep(1);
    self.allTicketCount --;
    NSLog(@"还有%ld张票 -- %@",self.allTicketCount,[NSThread currentThread]);
    
    // 解锁
    [self.lock unlock];
}
@end
  • 运行结果


    结果
7.NSRecursiveLock
  • 介绍
    对pthread_mutex 递归锁的封装
  • 代码
#import "ViewController.h"
@interface ViewController ()
@property (nonatomic, strong) NSRecursiveLock *lock;
@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    
    self.lock = [[NSRecursiveLock alloc] init];

    [self otherTest];

}


- (void)otherTest {
    
    // 加锁
    [self.lock lock];
    
    static NSInteger count = 0;
    if (count < 10) {
        count ++;
        NSLog(@" -- %ld",count);
        [self otherTest];
    }
    // 解锁
    [self.lock unlock];
    
}
@end
  • 运行结果


    结果
8.NSCondition
  • 介绍
    pthread_mutex和pthread_cond的封装
  • 代码
#import "ViewController.h"
@interface ViewController ()
@property (nonatomic, strong) NSCondition *cond;
@property (nonatomic, strong) NSMutableArray *data;
@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    
    self.cond = [[NSCondition alloc] init];
    self.data = [NSMutableArray array];
    
    dispatch_queue_t queue = dispatch_queue_create("queue", DISPATCH_QUEUE_CONCURRENT);
    dispatch_async(queue, ^{
        for (NSInteger i = 0; i < 5; i ++) {
            [self add];
        }
    });
    dispatch_async(queue, ^{
        for (NSInteger i = 0; i < 5; i ++) {
            [self remove];
        }
    });

}


- (void)add {
    
    [self.cond lock];
    
    [self.data addObject:@"test"];
    
    NSLog(@"add - %@",[NSThread currentThread]);

    [self.cond unlock];
}

- (void)remove {
    
    [self.cond lock];
    
    if (self.data.count == 0) {
        [self.cond wait];
    }
    
    [self.data removeLastObject];
    
    NSLog(@"remove - %@",[NSThread currentThread]);
    
    [self.cond unlock];
}
@end

  • 运行结果


    结果
9.NSConditionLock
  • 介绍
    NSCondition的进一步封装,可以设置具体的条件值
  • 代码
#import "ViewController.h"
@interface ViewController ()
@property (nonatomic, strong) NSConditionLock *condLock;
@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    
    self.condLock = [[NSConditionLock alloc] initWithCondition:1];
    
    dispatch_queue_t queue = dispatch_queue_create("queue", DISPATCH_QUEUE_CONCURRENT);
    dispatch_async(queue, ^{
        [self one];
    });
    dispatch_async(queue, ^{
        [self two];
    });
    dispatch_async(queue, ^{
        [self three];
    });
    
}


- (void)one {
    
    [self.condLock lock];
    
    NSLog(@"one");

    [self.condLock unlockWithCondition:2];
}

- (void)two {
    
    [self.condLock lockWhenCondition:2];
    
    NSLog(@"two");
    
    [self.condLock unlockWithCondition:3];
    
}

- (void)three {
    
    [self.condLock lockWhenCondition:3];
    
    NSLog(@"three");
    
    [self.condLock unlock];
}
@end
  • 运行结果


    结果
10.dispatch_semaphore_t 信号量
  • 介绍
    1.信号量的初始值,可以用来控制线程并发访问的最大数量;
    2.信号量的初始值为1,代表同时只允许1条线程访问资源,保证线程同步;
  • 代码
#import "ViewController.h"
@interface ViewController ()
@property (nonatomic, strong) dispatch_semaphore_t sema;
@property (nonatomic, assign) NSInteger ticketCount;

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    
    self.sema = dispatch_semaphore_create(1);
    self.ticketCount = 10;
    
    dispatch_queue_t queue = dispatch_queue_create("queue", DISPATCH_QUEUE_CONCURRENT);
    dispatch_async(queue, ^{
        for (NSInteger i = 0; i < 5; i ++) {
            [self sellTicket];
        }
    });
    dispatch_async(queue, ^{
        for (NSInteger i = 0; i < 5; i ++) {
            [self sellTicket];
        }
    });
}


- (void)sellTicket {
    
    dispatch_semaphore_wait(self.sema, DISPATCH_TIME_FOREVER);
    
    sleep(1);
    
    self.ticketCount --;
    
    NSLog(@" 还有%ld张票 -- %@",self.ticketCount,[NSThread currentThread]);
    
    dispatch_semaphore_signal(self.sema);
}
@end
  • 运行结果


    结果
11.@synchronized
  • 介绍
    1.@synchronized是对mutex递归锁的封装;
    2.@synchronized(obj)内部会生成obj对应的递归锁,然后进行加锁、解锁操作;
  • 代码
#import "ViewController.h"
@interface ViewController ()
@property (nonatomic, strong) dispatch_semaphore_t sema;
@property (nonatomic, assign) NSInteger ticketCount;

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    
    self.sema = dispatch_semaphore_create(1);
    self.ticketCount = 10;
    
    dispatch_queue_t queue = dispatch_queue_create("queue", DISPATCH_QUEUE_CONCURRENT);
    dispatch_async(queue, ^{
        for (NSInteger i = 0; i < 5; i ++) {
            [self sellTicket];
        }
    });
    dispatch_async(queue, ^{
        for (NSInteger i = 0; i < 5; i ++) {
            [self sellTicket];
        }
    });
}


- (void)sellTicket {
    
    @synchronized (self) {
        
        sleep(1);
        
        self.ticketCount --;
        
        NSLog(@" 还有%ld张票 -- %@",self.ticketCount,[NSThread currentThread]);

    }
}
@end
  • 运行结果


    结果
4.同步方案性能对比
  • 性能从高到低
os_unfair_lock
OSSpinLock
dispatch_semaphore
pthread_mutex
dispatch_queue(DISPATCH_QUEUE_SERIAL)
NSLock
NSCondition
pthread_mutex(recursive)
NSRecursiveLock
NSConditionLock
@synchronized

参考:MJ老师的视频课程。

相关文章

  • 多线程相关面试问题

    多线程:GCD、NSOperation、NSThread。 一、GCD 1、同步/异步 和 串行/并发(1)、同步...

  • iOS面试之多线程模块

    多线程 多线程内容如下: GCD NSOperation NSThread 多线程与锁 1.GCD 同步/异步和串...

  • GCD中的串行,并行,同步,异步详解

    gcd中的串行,并行,同步,异步详解 1、gcd中同步与异步的区别: gcd中的线程是由一个线程池来管理的,这个池...

  • [iOS面试]第6章 多线程相关面试问题

    本文主讲多线程相关面试问题:包括GCD、NSOperation、NSThread、多线程与锁。 一、GCD 同步/...

  • iOS面试 -- 多线程相关

    多线程技术方案 GCD NSOperation NSThread GCD 同步/异步 和串行/并发 dispatc...

  • iOS gcd看我就够了

    gcd是iOS中多线程的一种技术,下面我们来看看gcd的用法 同步&异步 同步 同步执行:字面意思,同步执行就是执...

  • iOS之GCD学习心得

    1.GCD简介 gcd有两大概念:任务和队列(1) 任务:同步任务和异步任务。同步任务:不会开辟线程,在当前线程执...

  • 多线程

    iOS中的几种多线程GCD1、GCD分为任务和队列,任务(同步,异步)队列(串行,并发),同步串行,同步主队列的情...

  • 多线程

    GCD NSOperation NSThread 多线程与锁 一、GCD 同步、异步 和 串行、并发 dispat...

  • 多线程

    ios中为我们提供了GCD、NSOpration、NSThread 一、GCD多线程与锁GCD 同步异步、串行、并...

网友评论

      本文标题:GCD线程同步

      本文链接:https://www.haomeiwen.com/subject/mpvflqtx.html