GCD线程同步

2018-12-25  本文已影响0人  xxxxxxxxx_ios

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

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

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

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

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

1.OSSpinLock
#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
#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
#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
#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
#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
#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
#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 信号量
#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
#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老师的视频课程。

上一篇 下一篇

猜你喜欢

热点阅读