多线程(二)线程同步

一、线程同步

  • 一块资源可能会被多个线程共享,也就是多个线程可能会访问同一块资源。当多个线程访问同一块资源时,很容易引发数据错乱和数据安全问题。
  • 下面通过一个经典的例子来演示多线程引发的数据错乱和数据安全问题

实践一:银行存钱取钱

  • 1、场景
    • 初始阶段我们给账户的余额100,然后每次调用存钱方法时给账户存入50,每次调用取钱方法时给账户取出20,所以执行完代码最后账户余额应该是400
  • 2、测试代码

#import "ViewController.h"

@interface ViewController ()
@property (assign, nonatomic) int money;
@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.
}


- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
    [self moneyTest];
}

/**
 存钱、取钱演示
 */
- (void)moneyTest {
    //初始账户余额有100
    self.money = 100;
    //全局队列
    dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
    //多个线程存钱
    dispatch_async(queue, ^{
        for (int i = 0; i < 10; i++) {
            [self saveMoney];
        }
    });
    
    //多个线程取钱
    dispatch_async(queue, ^{
        for (int i = 0; i < 10; i++) {
            [self drawMoney];
        }
    });
}

/**
 存钱
 */
- (void)saveMoney {
    int oldMoney = self.money;
    sleep(.2);
    oldMoney += 50;
    self.money = oldMoney;
    NSLog(@"存50,还剩%d元 - %@", oldMoney, [NSThread currentThread]);
}

/**
 取钱
 */
- (void)drawMoney {
    int oldMoney = self.money;
    sleep(.2);
    oldMoney -= 20;
    self.money = oldMoney;
    NSLog(@"取20,还剩%d元 - %@", oldMoney, [NSThread currentThread]);
}

@end
  • 3、打印输出
2018-07-22 14:38:19.243054+0800 03-多线程[25249:1401125] 取20,还剩80元 - {number = 3, name = (null)}
2018-07-22 14:38:19.243097+0800 03-多线程[25249:1401122] 存50,还剩150元 - {number = 4, name = (null)}
2018-07-22 14:38:19.243964+0800 03-多线程[25249:1401125] 取20,还剩130元 - {number = 3, name = (null)}
2018-07-22 14:38:19.244640+0800 03-多线程[25249:1401122] 存50,还剩180元 - {number = 4, name = (null)}
2018-07-22 14:38:19.244791+0800 03-多线程[25249:1401125] 取20,还剩160元 - {number = 3, name = (null)}
2018-07-22 14:38:19.244935+0800 03-多线程[25249:1401122] 存50,还剩210元 - {number = 4, name = (null)}
2018-07-22 14:38:19.244987+0800 03-多线程[25249:1401125] 取20,还剩190元 - {number = 3, name = (null)}
2018-07-22 14:38:19.245093+0800 03-多线程[25249:1401125] 取20,还剩220元 - {number = 3, name = (null)}
2018-07-22 14:38:19.245112+0800 03-多线程[25249:1401122] 存50,还剩240元 - {number = 4, name = (null)}
2018-07-22 14:38:19.245393+0800 03-多线程[25249:1401125] 取20,还剩200元 - {number = 3, name = (null)}
2018-07-22 14:38:19.245422+0800 03-多线程[25249:1401122] 存50,还剩250元 - {number = 4, name = (null)}
2018-07-22 14:38:19.245885+0800 03-多线程[25249:1401125] 取20,还剩230元 - {number = 3, name = (null)}
2018-07-22 14:38:19.246953+0800 03-多线程[25249:1401122] 存50,还剩280元 - {number = 4, name = (null)}
2018-07-22 14:38:19.247187+0800 03-多线程[25249:1401125] 取20,还剩260元 - {number = 3, name = (null)}
2018-07-22 14:38:19.247360+0800 03-多线程[25249:1401122] 存50,还剩310元 - {number = 4, name = (null)}
2018-07-22 14:38:19.247693+0800 03-多线程[25249:1401125] 取20,还剩290元 - {number = 3, name = (null)}
2018-07-22 14:38:19.248440+0800 03-多线程[25249:1401122] 存50,还剩340元 - {number = 4, name = (null)}
2018-07-22 14:38:19.248792+0800 03-多线程[25249:1401125] 取20,还剩320元 - {number = 3, name = (null)}
2018-07-22 14:38:19.249391+0800 03-多线程[25249:1401122] 存50,还剩370元 - {number = 4, name = (null)}
2018-07-22 14:38:19.250224+0800 03-多线程[25249:1401122] 存50,还剩420元 - {number = 4, name = (null)}
  • 4、分析:从最后的打印输出可知最后余额是420(不是400),发现这个存钱取钱的过程发生了错乱
    多线程存取款.png

实践二:售票系统

  • 1、场景
    • 现在假设有15张电影票,有3个窗口来卖票并且3个窗口可以同步卖票,假设每个窗口买5张票,所以15张电影票可以全部卖完
  • 2、测试代码
#import "ViewController.h"

@interface ViewController ()
@property (assign, nonatomic) int money;
@property (assign, nonatomic) int ticketsCount;
@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.
}


- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
    [self ticketTest];
}

/**
 卖票演示
 */
- (void)ticketTest
{
    // 15张票
    self.ticketsCount = 15;
    
    dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
    // 窗口1
    dispatch_async(queue, ^{
        for (int i = 0; i < 5; i++) {
            [self saleTicket];
        }
    });
    // 窗口2
    dispatch_async(queue, ^{
        for (int i = 0; i < 5; i++) {
            [self saleTicket];
        }
    });
    // 窗口3
    dispatch_async(queue, ^{
        for (int i = 0; i < 5; i++) {
            [self saleTicket];
        }
    });
}

/**
 卖1张票
 */
- (void)saleTicket {
    int oldTicketsCount = self.ticketsCount;
    sleep(.2);
    oldTicketsCount--;
    self.ticketsCount = oldTicketsCount;
    NSLog(@"还剩%d张票 - %@", oldTicketsCount, [NSThread currentThread]);
}
@end
  • 3、打印输出
2018-07-22 15:19:20.643275+0800 03-多线程[25622:1428898] 还剩14张票 - {number = 4, name = (null)}
2018-07-22 15:19:20.643520+0800 03-多线程[25622:1428896] 还剩13张票 - {number = 5, name = (null)}
2018-07-22 15:19:20.643577+0800 03-多线程[25622:1428895] 还剩12张票 - {number = 3, name = (null)}
2018-07-22 15:19:20.648231+0800 03-多线程[25622:1428896] 还剩11张票 - {number = 5, name = (null)}
2018-07-22 15:19:20.648232+0800 03-多线程[25622:1428898] 还剩11张票 - {number = 4, name = (null)}
2018-07-22 15:19:20.648455+0800 03-多线程[25622:1428895] 还剩10张票 - {number = 3, name = (null)}
2018-07-22 15:19:20.648702+0800 03-多线程[25622:1428898] 还剩9张票 - {number = 4, name = (null)}
2018-07-22 15:19:20.648760+0800 03-多线程[25622:1428896] 还剩8张票 - {number = 5, name = (null)}
2018-07-22 15:19:20.648797+0800 03-多线程[25622:1428895] 还剩7张票 - {number = 3, name = (null)}
2018-07-22 15:19:20.648863+0800 03-多线程[25622:1428898] 还剩6张票 - {number = 4, name = (null)}
2018-07-22 15:19:20.651074+0800 03-多线程[25622:1428896] 还剩5张票 - {number = 5, name = (null)}
2018-07-22 15:19:20.651705+0800 03-多线程[25622:1428895] 还剩4张票 - {number = 3, name = (null)}
2018-07-22 15:19:20.652069+0800 03-多线程[25622:1428898] 还剩3张票 - {number = 4, name = (null)}
2018-07-22 15:19:20.652631+0800 03-多线程[25622:1428896] 还剩2张票 - {number = 5, name = (null)}
2018-07-22 15:19:20.652998+0800 03-多线程[25622:1428895] 还剩1张票 - {number = 3, name = (null)}
  • 4、分析:从打印输出来看最后还剩余1张票,所以这样也造成了数据的错乱
    多线程卖票.png
  • 小结:多线程安全隐患
    多线程安全隐患.png
  • 为了解决多线程访问同一块资源引起的数据错乱,需要引入线程同步技术

二、线程同步

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

线程同步.png

iOS中线程同步方案

  • OSSpinLockLock
  • os_unfair_lock
  • pthread_mutex
  • dispatch_semaphore
  • dispatch_queue(DISPATCH_QUEUE_SERIAL )
  • NSLock
  • NSRecursiveLock
  • NSCondition
  • NSConditionLock
  • @synchronized

1、OSSpinLockLock实践

  • OSSpinLockLock叫做自旋锁,等待所得线程会处于忙等(busy-wait)状态,一直占用着CPU资源
  • 目前已经不再安全,可能会出现优先级反转问题,如果等待锁的线程优先级较高,它会一直占用着CPU资源,优先级低的线程就无法释放锁
  • 1、测试代码
#import "ViewController.h"
#import 

@interface ViewController ()
@property (assign, nonatomic) int money;
@property (assign, nonatomic) int ticketsCount;

@property (assign, nonatomic) OSSpinLock moneyLock;
@property (assign, nonatomic) OSSpinLock ticketLock;
@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    //初始化
    self.moneyLock = OS_SPINLOCK_INIT;
    self.ticketLock = OS_SPINLOCK_INIT;
}


- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
    //卖票
    [self ticketTest];
    //存取钱
    [self moneyTest];
}

/**
 卖票演示
 */
- (void)ticketTest
{
    // 15张票
    self.ticketsCount = 15;
    
    dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
    // 窗口1
    dispatch_async(queue, ^{
        for (int i = 0; i < 5; i++) {
            [self saleTicket];
        }
    });
    // 窗口2
    dispatch_async(queue, ^{
        for (int i = 0; i < 5; i++) {
            [self saleTicket];
        }
    });
    // 窗口3
    dispatch_async(queue, ^{
        for (int i = 0; i < 5; i++) {
            [self saleTicket];
        }
    });
}

/**
 卖1张票
 */
- (void)saleTicket {
    //加锁
    OSSpinLockLock(&_ticketLock);
    
    int oldTicketsCount = self.ticketsCount;
    sleep(.2);
    oldTicketsCount--;
    self.ticketsCount = oldTicketsCount;
    NSLog(@"还剩%d张票 - %@", oldTicketsCount, [NSThread currentThread]);
    
    //解锁
    OSSpinLockUnlock(&_ticketLock);
}

/**
 存钱、取钱演示
 */
- (void)moneyTest {
    //初始账户余额有100
    self.money = 100;
    //全局队列
    dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
    //多个线程存钱
    dispatch_async(queue, ^{
        for (int i = 0; i < 10; i++) {
            [self saveMoney];
        }
    });

    //多个线程取钱
    dispatch_async(queue, ^{
        for (int i = 0; i < 10; i++) {
            [self drawMoney];
        }
    });
}

/**
 存钱
 */
- (void)saveMoney {
    
    OSSpinLockLock(&_moneyLock);
    int oldMoney = self.money;
    sleep(.2);
    oldMoney += 50;
    self.money = oldMoney;
    NSLog(@"存50,还剩%d元 - %@", oldMoney, [NSThread currentThread]);
    OSSpinLockUnlock(&_moneyLock);
}

/**
 取钱
 */
- (void)drawMoney {
    OSSpinLockLock(&_moneyLock);
    int oldMoney = self.money;
    sleep(.2);
    oldMoney -= 20;
    self.money = oldMoney;
    NSLog(@"取20,还剩%d元 - %@", oldMoney, [NSThread currentThread]);
    OSSpinLockUnlock(&_moneyLock);
}

@end

  • 2、打印输出
2018-07-22 21:16:46.094150+0800 04-线程同步[28751:1663872] 还剩14张票 - {number = 3, name = (null)}
2018-07-22 21:16:46.095026+0800 04-线程同步[28751:1663874] 存50,还剩150元 - {number = 4, name = (null)}
2018-07-22 21:16:46.097541+0800 04-线程同步[28751:1663872] 还剩13张票 - {number = 3, name = (null)}
2018-07-22 21:16:46.099152+0800 04-线程同步[28751:1663874] 存50,还剩200元 - {number = 4, name = (null)}
2018-07-22 21:16:46.099555+0800 04-线程同步[28751:1663874] 存50,还剩250元 - {number = 4, name = (null)}
2018-07-22 21:16:46.099564+0800 04-线程同步[28751:1663872] 还剩12张票 - {number = 3, name = (null)}
2018-07-22 21:16:46.100243+0800 04-线程同步[28751:1663874] 存50,还剩300元 - {number = 4, name = (null)}
2018-07-22 21:16:46.100274+0800 04-线程同步[28751:1663872] 还剩11张票 - {number = 3, name = (null)}
2018-07-22 21:16:46.100709+0800 04-线程同步[28751:1663874] 存50,还剩350元 - {number = 4, name = (null)}
2018-07-22 21:16:46.101413+0800 04-线程同步[28751:1663872] 还剩10张票 - {number = 3, name = (null)}
2018-07-22 21:16:46.102132+0800 04-线程同步[28751:1663874] 存50,还剩400元 - {number = 4, name = (null)}
2018-07-22 21:16:46.102736+0800 04-线程同步[28751:1663874] 存50,还剩450元 - {number = 4, name = (null)}
2018-07-22 21:16:46.103384+0800 04-线程同步[28751:1663874] 存50,还剩500元 - {number = 4, name = (null)}
2018-07-22 21:16:46.103686+0800 04-线程同步[28751:1663874] 存50,还剩550元 - {number = 4, name = (null)}
2018-07-22 21:16:46.104295+0800 04-线程同步[28751:1663874] 存50,还剩600元 - {number = 4, name = (null)}
2018-07-22 21:16:46.105408+0800 04-线程同步[28751:1663875] 还剩9张票 - {number = 5, name = (null)}
2018-07-22 21:16:46.105565+0800 04-线程同步[28751:1663875] 还剩8张票 - {number = 5, name = (null)}
2018-07-22 21:16:46.105714+0800 04-线程同步[28751:1663875] 还剩7张票 - {number = 5, name = (null)}
2018-07-22 21:16:46.105827+0800 04-线程同步[28751:1663875] 还剩6张票 - {number = 5, name = (null)}
2018-07-22 21:16:46.105958+0800 04-线程同步[28751:1663875] 还剩5张票 - {number = 5, name = (null)}
2018-07-22 21:16:46.107031+0800 04-线程同步[28751:1663907] 取20,还剩580元 - {number = 6, name = (null)}
2018-07-22 21:16:46.107246+0800 04-线程同步[28751:1663907] 取20,还剩560元 - {number = 6, name = (null)}
2018-07-22 21:16:46.107480+0800 04-线程同步[28751:1663907] 取20,还剩540元 - {number = 6, name = (null)}
2018-07-22 21:16:46.107686+0800 04-线程同步[28751:1663907] 取20,还剩520元 - {number = 6, name = (null)}
2018-07-22 21:16:46.107789+0800 04-线程同步[28751:1663907] 取20,还剩500元 - {number = 6, name = (null)}
2018-07-22 21:16:46.108279+0800 04-线程同步[28751:1663907] 取20,还剩480元 - {number = 6, name = (null)}
2018-07-22 21:16:46.108566+0800 04-线程同步[28751:1663907] 取20,还剩460元 - {number = 6, name = (null)}
2018-07-22 21:16:46.108712+0800 04-线程同步[28751:1663907] 取20,还剩440元 - {number = 6, name = (null)}
2018-07-22 21:16:46.108849+0800 04-线程同步[28751:1663907] 取20,还剩420元 - {number = 6, name = (null)}
2018-07-22 21:16:46.109188+0800 04-线程同步[28751:1663907] 取20,还剩400元 - {number = 6, name = (null)}
2018-07-22 21:16:46.111072+0800 04-线程同步[28751:1663871] 还剩4张票 - {number = 7, name = (null)}
2018-07-22 21:16:46.111261+0800 04-线程同步[28751:1663871] 还剩3张票 - {number = 7, name = (null)}
2018-07-22 21:16:46.111386+0800 04-线程同步[28751:1663871] 还剩2张票 - {number = 7, name = (null)}
2018-07-22 21:16:46.111505+0800 04-线程同步[28751:1663871] 还剩1张票 - {number = 7, name = (null)}
2018-07-22 21:16:46.111659+0800 04-线程同步[28751:1663871] 还剩0张票 - {number = 7, name = (null)}
  • 分析
    • 从打印输出来看15张票全部都卖出了;最后的余额也是400;
    • 由于卖票和存取钱是2个任务,所以要创建2个锁

2、os_unfair_lock实践

  • os_unfair_lock用于取代不安全的OSSpinLock,从iOS10开始才支持
  • 从底层调用来看,等待os_unfair_lock锁的线程会处于休眠状态,并非忙等
  • 测试代码
#import "ViewController.h"
#import 

@interface ViewController ()
@property (assign, nonatomic) int money;
@property (assign, nonatomic) int ticketsCount;

@property (assign, nonatomic) os_unfair_lock moneyLock;
@property (assign, nonatomic) os_unfair_lock ticketLock;
@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    //初始化
    self.moneyLock = OS_UNFAIR_LOCK_INIT;
    self.ticketLock = OS_UNFAIR_LOCK_INIT;
}


- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
    //卖票
    [self ticketTest];
    //存取钱
    [self moneyTest];
}

/**
 卖票演示
 */
- (void)ticketTest
{
    // 15张票
    self.ticketsCount = 15;
    
    dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
    // 窗口1
    dispatch_async(queue, ^{
        for (int i = 0; i < 5; i++) {
            [self saleTicket];
        }
    });
    // 窗口2
    dispatch_async(queue, ^{
        for (int i = 0; i < 5; i++) {
            [self saleTicket];
        }
    });
    // 窗口3
    dispatch_async(queue, ^{
        for (int i = 0; i < 5; i++) {
            [self saleTicket];
        }
    });
}

/**
 卖1张票
 */
- (void)saleTicket {
    //加锁
    os_unfair_lock_lock(&_ticketLock);
    
    int oldTicketsCount = self.ticketsCount;
    sleep(.2);
    oldTicketsCount--;
    self.ticketsCount = oldTicketsCount;
    NSLog(@"还剩%d张票 - %@", oldTicketsCount, [NSThread currentThread]);
    
    //解锁
    os_unfair_lock_unlock(&_ticketLock);
}

/**
 存钱、取钱演示
 */
- (void)moneyTest {
    //初始账户余额有100
    self.money = 100;
    //全局队列
    dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
    //多个线程存钱
    dispatch_async(queue, ^{
        for (int i = 0; i < 10; i++) {
            [self saveMoney];
        }
    });

    //多个线程取钱
    dispatch_async(queue, ^{
        for (int i = 0; i < 10; i++) {
            [self drawMoney];
        }
    });
}

/**
 存钱
 */
- (void)saveMoney {
    
    os_unfair_lock_lock(&_moneyLock);
    int oldMoney = self.money;
    sleep(.2);
    oldMoney += 50;
    self.money = oldMoney;
    NSLog(@"存50,还剩%d元 - %@", oldMoney, [NSThread currentThread]);
    os_unfair_lock_unlock(&_moneyLock);
}

/**
 取钱
 */
- (void)drawMoney {
    os_unfair_lock_lock(&_moneyLock);
    int oldMoney = self.money;
    sleep(.2);
    oldMoney -= 20;
    self.money = oldMoney;
    NSLog(@"取20,还剩%d元 - %@", oldMoney, [NSThread currentThread]);
    os_unfair_lock_unlock(&_moneyLock);
}

@end
  • 打印输出
2018-07-22 21:28:21.805974+0800 04-线程同步[28936:1673956] 还剩14张票 - {number = 4, name = (null)}
2018-07-22 21:28:21.805974+0800 04-线程同步[28936:1673959] 存50,还剩150元 - {number = 3, name = (null)}
2018-07-22 21:28:21.806360+0800 04-线程同步[28936:1673957] 还剩13张票 - {number = 5, name = (null)}
2018-07-22 21:28:21.807003+0800 04-线程同步[28936:1673959] 存50,还剩200元 - {number = 3, name = (null)}
2018-07-22 21:28:21.807003+0800 04-线程同步[28936:1673957] 还剩12张票 - {number = 5, name = (null)}
2018-07-22 21:28:21.807583+0800 04-线程同步[28936:1674091] 取20,还剩180元 - {number = 6, name = (null)}
2018-07-22 21:28:21.807605+0800 04-线程同步[28936:1673957] 还剩11张票 - {number = 5, name = (null)}
2018-07-22 21:28:21.807759+0800 04-线程同步[28936:1674091] 取20,还剩160元 - {number = 6, name = (null)}
2018-07-22 21:28:21.808154+0800 04-线程同步[28936:1673957] 还剩10张票 - {number = 5, name = (null)}
2018-07-22 21:28:21.809105+0800 04-线程同步[28936:1674091] 取20,还剩140元 - {number = 6, name = (null)}
2018-07-22 21:28:21.809871+0800 04-线程同步[28936:1673957] 还剩9张票 - {number = 5, name = (null)}
2018-07-22 21:28:21.810593+0800 04-线程同步[28936:1674091] 取20,还剩120元 - {number = 6, name = (null)}
2018-07-22 21:28:21.811036+0800 04-线程同步[28936:1673958] 还剩8张票 - {number = 7, name = (null)}
2018-07-22 21:28:21.811514+0800 04-线程同步[28936:1674091] 取20,还剩100元 - {number = 6, name = (null)}
2018-07-22 21:28:21.811874+0800 04-线程同步[28936:1673958] 还剩7张票 - {number = 7, name = (null)}
2018-07-22 21:28:21.812369+0800 04-线程同步[28936:1674091] 取20,还剩80元 - {number = 6, name = (null)}
2018-07-22 21:28:21.812739+0800 04-线程同步[28936:1673958] 还剩6张票 - {number = 7, name = (null)}
2018-07-22 21:28:21.813132+0800 04-线程同步[28936:1674091] 取20,还剩60元 - {number = 6, name = (null)}
2018-07-22 21:28:21.813661+0800 04-线程同步[28936:1673958] 还剩5张票 - {number = 7, name = (null)}
2018-07-22 21:28:21.813856+0800 04-线程同步[28936:1674091] 取20,还剩40元 - {number = 6, name = (null)}
2018-07-22 21:28:21.814099+0800 04-线程同步[28936:1673958] 还剩4张票 - {number = 7, name = (null)}
2018-07-22 21:28:21.814576+0800 04-线程同步[28936:1674091] 取20,还剩20元 - {number = 6, name = (null)}
2018-07-22 21:28:21.814942+0800 04-线程同步[28936:1673956] 还剩3张票 - {number = 4, name = (null)}
2018-07-22 21:28:21.815258+0800 04-线程同步[28936:1674091] 取20,还剩0元 - {number = 6, name = (null)}
2018-07-22 21:28:21.815480+0800 04-线程同步[28936:1673956] 还剩2张票 - {number = 4, name = (null)}
2018-07-22 21:28:21.815852+0800 04-线程同步[28936:1673959] 存50,还剩50元 - {number = 3, name = (null)}
2018-07-22 21:28:21.816118+0800 04-线程同步[28936:1673956] 还剩1张票 - {number = 4, name = (null)}
2018-07-22 21:28:21.816403+0800 04-线程同步[28936:1673959] 存50,还剩100元 - {number = 3, name = (null)}
2018-07-22 21:28:21.816708+0800 04-线程同步[28936:1673956] 还剩0张票 - {number = 4, name = (null)}
2018-07-22 21:28:21.816909+0800 04-线程同步[28936:1673959] 存50,还剩150元 - {number = 3, name = (null)}
2018-07-22 21:28:21.817441+0800 04-线程同步[28936:1673959] 存50,还剩200元 - {number = 3, name = (null)}
2018-07-22 21:28:21.817602+0800 04-线程同步[28936:1673959] 存50,还剩250元 - {number = 3, name = (null)}
2018-07-22 21:28:21.817879+0800 04-线程同步[28936:1673959] 存50,还剩300元 - {number = 3, name = (null)}
2018-07-22 21:28:21.818107+0800 04-线程同步[28936:1673959] 存50,还剩350元 - {number = 3, name = (null)}
2018-07-22 21:28:21.818385+0800 04-线程同步[28936:1673959] 存50,还剩400元 - {number = 3, name = (null)}
  • 分析
    • 从打印输出来看15张票全部都卖出了;最后的余额也是400;
    • 由于卖票和存取钱是2个任务,所以要创建2个锁

3、pthread_mutex

  • mutex叫做“互斥锁”,等待锁的线程会处于休眠状态
  • 1、测试代码

#import "ViewController.h"
#import 

@interface ViewController ()
@property (assign, nonatomic) int money;
@property (assign, nonatomic) int ticketsCount;

@property (assign, nonatomic) pthread_mutex_t moneyLock;
@property (assign, nonatomic) pthread_mutex_t ticketLock;
@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    //初始化
    [self __initMutex:&_ticketLock];
    [self __initMutex:&_moneyLock];
}

- (void)__initMutex:(pthread_mutex_t *)mutex
{
    // 静态初始化
    //        pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
    
    //    // 初始化属性
    //    pthread_mutexattr_t attr;
    //    pthread_mutexattr_init(&attr);
    //    pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_DEFAULT);
    //    // 初始化锁
    //    pthread_mutex_init(mutex, &attr);
    //    // 销毁属性
    //    pthread_mutexattr_destroy(&attr);
    
    // 初始化属性
    //    pthread_mutexattr_t attr;
    //    pthread_mutexattr_init(&attr);
    //    pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_DEFAULT);
    // 初始化锁
    pthread_mutex_init(mutex, NULL);
    // 销毁属性
    //    pthread_mutexattr_destroy(&attr);
}


- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
    //卖票
    [self ticketTest];
    //存取钱
    [self moneyTest];
}

/**
 卖票演示
 */
- (void)ticketTest
{
    // 15张票
    self.ticketsCount = 15;
    
    dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
    // 窗口1
    dispatch_async(queue, ^{
        for (int i = 0; i < 5; i++) {
            [self saleTicket];
        }
    });
    // 窗口2
    dispatch_async(queue, ^{
        for (int i = 0; i < 5; i++) {
            [self saleTicket];
        }
    });
    // 窗口3
    dispatch_async(queue, ^{
        for (int i = 0; i < 5; i++) {
            [self saleTicket];
        }
    });
}

/**
 卖1张票
 */
- (void)saleTicket {
    //加锁
    pthread_mutex_lock(&_ticketLock);
    
    int oldTicketsCount = self.ticketsCount;
    sleep(.2);
    oldTicketsCount--;
    self.ticketsCount = oldTicketsCount;
    NSLog(@"还剩%d张票 - %@", oldTicketsCount, [NSThread currentThread]);
    
    //解锁
    pthread_mutex_unlock(&_ticketLock);
}

/**
 存钱、取钱演示
 */
- (void)moneyTest {
    //初始账户余额有100
    self.money = 100;
    //全局队列
    dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
    //多个线程存钱
    dispatch_async(queue, ^{
        for (int i = 0; i < 10; i++) {
            [self saveMoney];
        }
    });

    //多个线程取钱
    dispatch_async(queue, ^{
        for (int i = 0; i < 10; i++) {
            [self drawMoney];
        }
    });
}

/**
 存钱
 */
- (void)saveMoney {
    pthread_mutex_lock(&_moneyLock);
    int oldMoney = self.money;
    sleep(.2);
    oldMoney += 50;
    self.money = oldMoney;
    NSLog(@"存50,还剩%d元 - %@", oldMoney, [NSThread currentThread]);
    pthread_mutex_unlock(&_moneyLock);
}

/**
 取钱
 */
- (void)drawMoney {
    
    pthread_mutex_lock(&_moneyLock);
    
    int oldMoney = self.money;
    sleep(.2);
    oldMoney -= 20;
    self.money = oldMoney;
    NSLog(@"取20,还剩%d元 - %@", oldMoney, [NSThread currentThread]);
    pthread_mutex_unlock(&_moneyLock);
}

- (void)dealloc
{
    pthread_mutex_destroy(&_moneyLock);
    pthread_mutex_destroy(&_ticketLock);
}


@end
  • 2、打印输出
2018-07-22 21:37:40.402815+0800 04-线程同步[29061:1682129] 存50,还剩150元 - {number = 5, name = (null)}
2018-07-22 21:37:40.402820+0800 04-线程同步[29061:1681939] 还剩14张票 - {number = 4, name = (null)}
2018-07-22 21:37:40.403269+0800 04-线程同步[29061:1682130] 取20,还剩130元 - {number = 6, name = (null)}
2018-07-22 21:37:40.403328+0800 04-线程同步[29061:1682123] 还剩13张票 - {number = 7, name = (null)}
2018-07-22 21:37:40.403532+0800 04-线程同步[29061:1682129] 存50,还剩180元 - {number = 5, name = (null)}
2018-07-22 21:37:40.403549+0800 04-线程同步[29061:1682128] 还剩12张票 - {number = 8, name = (null)}
2018-07-22 21:37:40.403742+0800 04-线程同步[29061:1682130] 取20,还剩160元 - {number = 6, name = (null)}
2018-07-22 21:37:40.404624+0800 04-线程同步[29061:1681939] 还剩11张票 - {number = 4, name = (null)}
2018-07-22 21:37:40.405049+0800 04-线程同步[29061:1682129] 存50,还剩210元 - {number = 5, name = (null)}
2018-07-22 21:37:40.405408+0800 04-线程同步[29061:1682123] 还剩10张票 - {number = 7, name = (null)}
2018-07-22 21:37:40.406570+0800 04-线程同步[29061:1682128] 还剩9张票 - {number = 8, name = (null)}
2018-07-22 21:37:40.407367+0800 04-线程同步[29061:1682130] 取20,还剩190元 - {number = 6, name = (null)}
2018-07-22 21:37:40.408014+0800 04-线程同步[29061:1681939] 还剩8张票 - {number = 4, name = (null)}
2018-07-22 21:37:40.408360+0800 04-线程同步[29061:1682129] 存50,还剩240元 - {number = 5, name = (null)}
2018-07-22 21:37:40.408822+0800 04-线程同步[29061:1682123] 还剩7张票 - {number = 7, name = (null)}
2018-07-22 21:37:40.409213+0800 04-线程同步[29061:1682130] 取20,还剩220元 - {number = 6, name = (null)}
2018-07-22 21:37:40.409615+0800 04-线程同步[29061:1682128] 还剩6张票 - {number = 8, name = (null)}
2018-07-22 21:37:40.410110+0800 04-线程同步[29061:1682129] 存50,还剩270元 - {number = 5, name = (null)}
2018-07-22 21:37:40.410470+0800 04-线程同步[29061:1681939] 还剩5张票 - {number = 4, name = (null)}
2018-07-22 21:37:40.410746+0800 04-线程同步[29061:1682130] 取20,还剩250元 - {number = 6, name = (null)}
2018-07-22 21:37:40.410943+0800 04-线程同步[29061:1682123] 还剩4张票 - {number = 7, name = (null)}
2018-07-22 21:37:40.411215+0800 04-线程同步[29061:1682129] 存50,还剩300元 - {number = 5, name = (null)}
2018-07-22 21:37:40.411465+0800 04-线程同步[29061:1682128] 还剩3张票 - {number = 8, name = (null)}
2018-07-22 21:37:40.411803+0800 04-线程同步[29061:1682130] 取20,还剩280元 - {number = 6, name = (null)}
2018-07-22 21:37:40.412208+0800 04-线程同步[29061:1681939] 还剩2张票 - {number = 4, name = (null)}
2018-07-22 21:37:40.412514+0800 04-线程同步[29061:1682129] 存50,还剩330元 - {number = 5, name = (null)}
2018-07-22 21:37:40.413114+0800 04-线程同步[29061:1682123] 还剩1张票 - {number = 7, name = (null)}
2018-07-22 21:37:40.413512+0800 04-线程同步[29061:1682130] 取20,还剩310元 - {number = 6, name = (null)}
2018-07-22 21:37:40.414109+0800 04-线程同步[29061:1682128] 还剩0张票 - {number = 8, name = (null)}
2018-07-22 21:37:40.414502+0800 04-线程同步[29061:1682129] 存50,还剩360元 - {number = 5, name = (null)}
2018-07-22 21:37:40.415220+0800 04-线程同步[29061:1682130] 取20,还剩340元 - {number = 6, name = (null)}
2018-07-22 21:37:40.415415+0800 04-线程同步[29061:1682129] 存50,还剩390元 - {number = 5, name = (null)}
2018-07-22 21:37:40.415747+0800 04-线程同步[29061:1682130] 取20,还剩370元 - {number = 6, name = (null)}
2018-07-22 21:37:40.416032+0800 04-线程同步[29061:1682129] 存50,还剩420元 - {number = 5, name = (null)}
2018-07-22 21:37:40.416360+0800 04-线程同步[29061:1682130] 取20,还剩400元 - {number = 6, name = (null)}

4、pthread_mutex-递归锁

  • 1、测试代码
- (void)otherTest
{
    pthread_mutex_lock(&_mutex);
    
    NSLog(@"%s", __func__);
    
    static int count = 0;
    if (count < 10) {
        count++;
        [self otherTest];
    }
    
    pthread_mutex_unlock(&_mutex);
}

//- (void)otherTest2
//{
//    pthread_mutex_lock(&_mutex2);
//
//    NSLog(@"%s", __func__);
//
//    pthread_mutex_unlock(&_mutex2);
//}

- (void)dealloc
{
    pthread_mutex_destroy(&_mutex);
}
  • 2、打印输出
2018-07-22 21:46:28.764320+0800 04-线程同步[29196:1689512] -[ViewController otherTest]
2018-07-22 21:46:28.764607+0800 04-线程同步[29196:1689512] -[ViewController otherTest]
2018-07-22 21:46:28.765107+0800 04-线程同步[29196:1689512] -[ViewController otherTest]
2018-07-22 21:46:28.765431+0800 04-线程同步[29196:1689512] -[ViewController otherTest]
2018-07-22 21:46:28.766708+0800 04-线程同步[29196:1689512] -[ViewController otherTest]
2018-07-22 21:46:28.766904+0800 04-线程同步[29196:1689512] -[ViewController otherTest]
2018-07-22 21:46:28.767033+0800 04-线程同步[29196:1689512] -[ViewController otherTest]
2018-07-22 21:46:28.767144+0800 04-线程同步[29196:1689512] -[ViewController otherTest]
2018-07-22 21:46:28.767279+0800 04-线程同步[29196:1689512] -[ViewController otherTest]
2018-07-22 21:46:28.767371+0800 04-线程同步[29196:1689512] -[ViewController otherTest]
2018-07-22 21:46:28.767591+0800 04-线程同步[29196:1689512] -[ViewController otherTest]

5、pthread_mutex-条件锁

  • 1、场景
    • 数组中有内容后才可以进行删除操作
  • 2、测试代码

#import "ViewController.h"
#import 

@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];
    
    // 初始化属性
    pthread_mutexattr_t attr;
    pthread_mutexattr_init(&attr);
    pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
    // 初始化锁
    pthread_mutex_init(&_mutex, &attr);
    // 销毁属性
    pthread_mutexattr_destroy(&attr);
    
    // 初始化条件
    pthread_cond_init(&_cond, NULL);
    
    self.data = [NSMutableArray array];
    
}


- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
    [self otherTest];
}

- (void)otherTest
{
    [[[NSThread alloc] initWithTarget:self selector:@selector(__remove) object:nil] start];
    
    [[[NSThread alloc] initWithTarget:self selector:@selector(__add) object:nil] start];
}

// 生产者-消费者模式

// 线程1
// 删除数组中的元素
- (void)__remove
{
    pthread_mutex_lock(&_mutex);
    NSLog(@"__remove - begin");
    
    if (self.data.count == 0) {
        // 等待
        pthread_cond_wait(&_cond, &_mutex);
    }
    
    [self.data removeLastObject];
    NSLog(@"删除了元素");
    
    pthread_mutex_unlock(&_mutex);
}

// 线程2
// 往数组中添加元素
- (void)__add
{
    pthread_mutex_lock(&_mutex);
    
    sleep(1);
    
    [self.data addObject:@"Test"];
    NSLog(@"添加了元素");
    
    // 信号
    pthread_cond_signal(&_cond);
    // 广播
    //    pthread_cond_broadcast(&_cond);
    
    pthread_mutex_unlock(&_mutex);
}

- (void)dealloc
{
    pthread_mutex_destroy(&_mutex);
    pthread_cond_destroy(&_cond);
}
  • 3、打印输出
2018-07-22 21:53:38.489728+0800 04-线程同步[29291:1695769] __remove - begin
2018-07-22 21:53:39.495488+0800 04-线程同步[29291:1695770] 添加了元素
2018-07-22 21:53:39.495964+0800 04-线程同步[29291:1695769] 删除了元素

6、NSLock

  • NSLock是对mutex普通锁的封装
  • 测试代码

#import "ViewController.h"


@interface ViewController ()
@property (assign, nonatomic) int money;
@property (assign, nonatomic) int ticketsCount;

@property (nonatomic, strong) NSLock *moneyLock;
@property (nonatomic, strong) NSLock *ticketsLock;
@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    //初始化
    self.moneyLock = [[NSLock alloc] init];
    self.ticketsLock = [[NSLock alloc] init];
}


- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
    //卖票
    [self ticketTest];
    //存取钱
    [self moneyTest];
}

/**
 卖票演示
 */
- (void)ticketTest {
    // 15张票
    self.ticketsCount = 15;
    
    dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
    // 窗口1
    dispatch_async(queue, ^{
        for (int i = 0; i < 5; i++) {
            [self saleTicket];
        }
    });
    // 窗口2
    dispatch_async(queue, ^{
        for (int i = 0; i < 5; i++) {
            [self saleTicket];
        }
    });
    // 窗口3
    dispatch_async(queue, ^{
        for (int i = 0; i < 5; i++) {
            [self saleTicket];
        }
    });
}

/**
 卖1张票
 */
- (void)saleTicket {
    //加锁
    [self.ticketsLock lock];
    
    int oldTicketsCount = self.ticketsCount;
    sleep(.2);
    oldTicketsCount--;
    self.ticketsCount = oldTicketsCount;
    NSLog(@"还剩%d张票 - %@", oldTicketsCount, [NSThread currentThread]);
    
    //解锁
    [self.ticketsLock unlock];
}

/**
 存钱、取钱演示
 */
- (void)moneyTest {
    //初始账户余额有100
    self.money = 100;
    //全局队列
    dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
    //多个线程存钱
    dispatch_async(queue, ^{
        for (int i = 0; i < 10; i++) {
            [self saveMoney];
        }
    });

    //多个线程取钱
    dispatch_async(queue, ^{
        for (int i = 0; i < 10; i++) {
            [self drawMoney];
        }
    });
}

/**
 存钱
 */
- (void)saveMoney {
    
    [self.moneyLock lock];
    int oldMoney = self.money;
    sleep(.2);
    oldMoney += 50;
    self.money = oldMoney;
    NSLog(@"存50,还剩%d元 - %@", oldMoney, [NSThread currentThread]);
    
    [self.moneyLock unlock];
    
}

/**
 取钱
 */
- (void)drawMoney {
    
    [self.moneyLock lock];
    
    int oldMoney = self.money;
    sleep(.2);
    oldMoney -= 20;
    self.money = oldMoney;
    NSLog(@"取20,还剩%d元 - %@", oldMoney, [NSThread currentThread]);
    [self.moneyLock unlock];
}

@end

  • 打印输出
2018-07-22 22:01:38.569355+0800 04-线程同步[29420:1702742] 存50,还剩150元 - {number = 5, name = (null)}
2018-07-22 22:01:38.569356+0800 04-线程同步[29420:1702439] 还剩14张票 - {number = 4, name = (null)}
2018-07-22 22:01:38.569755+0800 04-线程同步[29420:1702736] 还剩13张票 - {number = 7, name = (null)}
2018-07-22 22:01:38.569755+0800 04-线程同步[29420:1702743] 取20,还剩130元 - {number = 6, name = (null)}
2018-07-22 22:01:38.570471+0800 04-线程同步[29420:1702741] 还剩12张票 - {number = 8, name = (null)}
2018-07-22 22:01:38.570685+0800 04-线程同步[29420:1702742] 存50,还剩180元 - {number = 5, name = (null)}
2018-07-22 22:01:38.570976+0800 04-线程同步[29420:1702439] 还剩11张票 - {number = 4, name = (null)}
2018-07-22 22:01:38.571240+0800 04-线程同步[29420:1702743] 取20,还剩160元 - {number = 6, name = (null)}
2018-07-22 22:01:38.571269+0800 04-线程同步[29420:1702736] 还剩10张票 - {number = 7, name = (null)}
2018-07-22 22:01:38.572278+0800 04-线程同步[29420:1702742] 存50,还剩210元 - {number = 5, name = (null)}
2018-07-22 22:01:38.572664+0800 04-线程同步[29420:1702741] 还剩9张票 - {number = 8, name = (null)}
2018-07-22 22:01:38.573125+0800 04-线程同步[29420:1702743] 取20,还剩190元 - {number = 6, name = (null)}
2018-07-22 22:01:38.573545+0800 04-线程同步[29420:1702439] 还剩8张票 - {number = 4, name = (null)}
2018-07-22 22:01:38.574068+0800 04-线程同步[29420:1702742] 存50,还剩240元 - {number = 5, name = (null)}
2018-07-22 22:01:38.574424+0800 04-线程同步[29420:1702736] 还剩7张票 - {number = 7, name = (null)}
2018-07-22 22:01:38.574715+0800 04-线程同步[29420:1702743] 取20,还剩220元 - {number = 6, name = (null)}
2018-07-22 22:01:38.575121+0800 04-线程同步[29420:1702741] 还剩6张票 - {number = 8, name = (null)}
2018-07-22 22:01:38.575613+0800 04-线程同步[29420:1702742] 存50,还剩270元 - {number = 5, name = (null)}
2018-07-22 22:01:38.575960+0800 04-线程同步[29420:1702439] 还剩5张票 - {number = 4, name = (null)}
2018-07-22 22:01:38.576419+0800 04-线程同步[29420:1702743] 取20,还剩250元 - {number = 6, name = (null)}
2018-07-22 22:01:38.576610+0800 04-线程同步[29420:1702736] 还剩4张票 - {number = 7, name = (null)}
2018-07-22 22:01:38.576876+0800 04-线程同步[29420:1702742] 存50,还剩300元 - {number = 5, name = (null)}
2018-07-22 22:01:38.577212+0800 04-线程同步[29420:1702741] 还剩3张票 - {number = 8, name = (null)}
2018-07-22 22:01:38.577505+0800 04-线程同步[29420:1702743] 取20,还剩280元 - {number = 6, name = (null)}
2018-07-22 22:01:38.577783+0800 04-线程同步[29420:1702439] 还剩2张票 - {number = 4, name = (null)}
2018-07-22 22:01:38.578217+0800 04-线程同步[29420:1702742] 存50,还剩330元 - {number = 5, name = (null)}
2018-07-22 22:01:38.578519+0800 04-线程同步[29420:1702736] 还剩1张票 - {number = 7, name = (null)}
2018-07-22 22:01:38.578806+0800 04-线程同步[29420:1702743] 取20,还剩310元 - {number = 6, name = (null)}
2018-07-22 22:01:38.579097+0800 04-线程同步[29420:1702741] 还剩0张票 - {number = 8, name = (null)}
2018-07-22 22:01:38.579348+0800 04-线程同步[29420:1702742] 存50,还剩360元 - {number = 5, name = (null)}
2018-07-22 22:01:38.579820+0800 04-线程同步[29420:1702743] 取20,还剩340元 - {number = 6, name = (null)}
2018-07-22 22:01:38.580072+0800 04-线程同步[29420:1702742] 存50,还剩390元 - {number = 5, name = (null)}
2018-07-22 22:01:38.580370+0800 04-线程同步[29420:1702743] 取20,还剩370元 - {number = 6, name = (null)}
2018-07-22 22:01:38.580725+0800 04-线程同步[29420:1702742] 存50,还剩420元 - {number = 5, name = (null)}
2018-07-22 22:01:38.580961+0800 04-线程同步[29420:1702743] 取20,还剩400元 - {number = 6, name = (null)}

7、NSRecursiveLock递归锁

  • NSRecursiveLock是对mutex递归锁的封装
  • 测试代码

#import "ViewController.h"


@interface ViewController ()
@property (strong, nonatomic) NSRecursiveLock *recursiveLock;
@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    //初始化
    self.recursiveLock = [[NSRecursiveLock alloc] init];
}

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
    [self otherTest];
}

- (void)otherTest
{
    [self.recursiveLock lock];
    
    NSLog(@"%s", __func__);
    
    static int count = 0;
    if (count < 10) {
        count++;
        [self otherTest];
    }
    
    [self.recursiveLock unlock];
}

@end

  • 打印输出
2018-07-22 22:05:29.119887+0800 04-线程同步[29492:1706958] -[ViewController otherTest]
2018-07-22 22:05:29.120549+0800 04-线程同步[29492:1706958] -[ViewController otherTest]
2018-07-22 22:05:29.121757+0800 04-线程同步[29492:1706958] -[ViewController otherTest]
2018-07-22 22:05:29.121875+0800 04-线程同步[29492:1706958] -[ViewController otherTest]
2018-07-22 22:05:29.122006+0800 04-线程同步[29492:1706958] -[ViewController otherTest]
2018-07-22 22:05:29.122121+0800 04-线程同步[29492:1706958] -[ViewController otherTest]
2018-07-22 22:05:29.122213+0800 04-线程同步[29492:1706958] -[ViewController otherTest]
2018-07-22 22:05:29.122397+0800 04-线程同步[29492:1706958] -[ViewController otherTest]
2018-07-22 22:05:29.122596+0800 04-线程同步[29492:1706958] -[ViewController otherTest]
2018-07-22 22:05:29.122696+0800 04-线程同步[29492:1706958] -[ViewController otherTest]
2018-07-22 22:05:29.122836+0800 04-线程同步[29492:1706958] -[ViewController otherTest]

8、NSCondition

  • 测试代码
#import "ViewController.h"

@interface ViewController ()
@property (strong, nonatomic) NSCondition *condition;
@property (strong, nonatomic) NSMutableArray *data;

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    
    self.condition = [[NSCondition alloc] init];
    self.data = [NSMutableArray array];
    
}


- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
    [self otherTest];
}

- (void)otherTest
{
    [[[NSThread alloc] initWithTarget:self selector:@selector(__remove) object:nil] start];
    
    [[[NSThread alloc] initWithTarget:self selector:@selector(__add) object:nil] start];
}

// 生产者-消费者模式

// 线程1
// 删除数组中的元素
- (void)__remove
{
    [self.condition lock];
    NSLog(@"__remove - begin");
    
    if (self.data.count == 0) {
        // 等待
        [self.condition wait];
    }
    
    [self.data removeLastObject];
    NSLog(@"删除了元素");
    
    [self.condition unlock];
}

// 线程2
// 往数组中添加元素
- (void)__add
{
    [self.condition lock];
    
    sleep(1);
    
    [self.data addObject:@"Test"];
    NSLog(@"添加了元素");
    
    // 信号
    [self.condition signal];
    
    sleep(2);
    
    [self.condition unlock];
}

@end

  • 打印输出
2018-07-22 22:11:46.999520+0800 04-线程同步[29631:1713131] __remove - begin
2018-07-22 22:11:48.002440+0800 04-线程同步[29631:1713132] 添加了元素
2018-07-22 22:11:50.007164+0800 04-线程同步[29631:1713131] 删除了元素

9、NSConditionLock

  • NSConditionLock是对NSCondition的进一步封装,可以设置具体的条件值
  • 可以控制线程的次序
  • 测试代码

#import "ViewController.h"

@interface ViewController ()
@property (strong, nonatomic) NSConditionLock *conditionLock;

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    //1就是一个线程条件
    self.conditionLock = [[NSConditionLock alloc] initWithCondition:1];

}


- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
    [self otherTest];
}
- (void)otherTest
{
    [[[NSThread alloc] initWithTarget:self selector:@selector(__one) object:nil] start];
    
    [[[NSThread alloc] initWithTarget:self selector:@selector(__two) object:nil] start];
    
    [[[NSThread alloc] initWithTarget:self selector:@selector(__three) object:nil] start];
}

- (void)__one
{
    [self.conditionLock lock];
    
    NSLog(@"__one");
    sleep(1);
    
    [self.conditionLock unlockWithCondition:2];
}

- (void)__two
{
    [self.conditionLock lockWhenCondition:2];
    
    NSLog(@"__two");
    sleep(1);
    
    [self.conditionLock unlockWithCondition:3];
}

- (void)__three
{
    [self.conditionLock lockWhenCondition:3];
    
    NSLog(@"__three");
    
    [self.conditionLock unlock];
}

@end
  • 打印输出
2018-07-22 22:16:10.519371+0800 04-线程同步[29707:1717148] __one
2018-07-22 22:16:11.524083+0800 04-线程同步[29707:1717149] __two
2018-07-22 22:16:12.526615+0800 04-线程同步[29707:1717150] __three

10、使用串行队列现线程同步

  • 测试代码

#import "ViewController.h"

@interface ViewController ()

@property (assign, nonatomic) int ticketsCount;
@property (assign, nonatomic) int money;

@property (strong, nonatomic) dispatch_queue_t ticketQueue;
@property (strong, nonatomic) dispatch_queue_t moneyQueue;

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    
    //必须是手动创建串行队列
    self.ticketQueue = dispatch_queue_create("ticketQueue", DISPATCH_QUEUE_SERIAL);
    self.moneyQueue = dispatch_queue_create("moneyQueue", DISPATCH_QUEUE_SERIAL);

}


- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
    [self ticketTest];
    [self moneyTest];
}

/**
 卖票演示
 */
- (void)ticketTest
{
    // 15张票
    self.ticketsCount = 15;
    
    dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
    // 窗口1
    dispatch_async(queue, ^{
        for (int i = 0; i < 5; i++) {
            [self saleTicket];
        }
    });
    // 窗口2
    dispatch_async(queue, ^{
        for (int i = 0; i < 5; i++) {
            [self saleTicket];
        }
    });
    // 窗口3
    dispatch_async(queue, ^{
        for (int i = 0; i < 5; i++) {
            [self saleTicket];
        }
    });
}

/**
 卖1张票
 */
- (void)saleTicket {
    dispatch_sync(self.ticketQueue, ^{
        int oldTicketsCount = self.ticketsCount;
        sleep(.2);
        oldTicketsCount--;
        self.ticketsCount = oldTicketsCount;
        NSLog(@"还剩%d张票 - %@", oldTicketsCount, [NSThread currentThread]);
    });
}

/**
 存钱、取钱演示
 */
- (void)moneyTest {
    //初始账户余额有100
    self.money = 100;
    //全局队列
    dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
    //多个线程存钱
    dispatch_async(queue, ^{
        for (int i = 0; i < 10; i++) {
            [self saveMoney];
        }
    });
    
    //多个线程取钱
    dispatch_async(queue, ^{
        for (int i = 0; i < 10; i++) {
            [self drawMoney];
        }
    });
}

/**
 存钱
 */
- (void)saveMoney {
    dispatch_sync(self.moneyQueue, ^{
        int oldMoney = self.money;
        sleep(.2);
        oldMoney += 50;
        self.money = oldMoney;
        NSLog(@"存50,还剩%d元 - %@", oldMoney, [NSThread currentThread]);
    });
}

/**
 取钱
 */
- (void)drawMoney {
    dispatch_sync(self.moneyQueue, ^{
        int oldMoney = self.money;
        sleep(.2);
        oldMoney -= 20;
        self.money = oldMoney;
        NSLog(@"取20,还剩%d元 - %@", oldMoney, [NSThread currentThread]);
    });
}

@end

  • 打印输出
2018-07-22 22:26:23.152779+0800 04-线程同步[29857:1725678] 还剩14张票 - {number = 4, name = (null)}
2018-07-22 22:26:23.153157+0800 04-线程同步[29857:1725854] 存50,还剩150元 - {number = 5, name = (null)}
2018-07-22 22:26:23.153878+0800 04-线程同步[29857:1725848] 还剩13张票 - {number = 6, name = (null)}
2018-07-22 22:26:23.154249+0800 04-线程同步[29857:1725855] 取20,还剩130元 - {number = 7, name = (null)}
2018-07-22 22:26:23.154332+0800 04-线程同步[29857:1725853] 还剩12张票 - {number = 8, name = (null)}
2018-07-22 22:26:23.154935+0800 04-线程同步[29857:1725854] 存50,还剩180元 - {number = 5, name = (null)}
2018-07-22 22:26:23.155617+0800 04-线程同步[29857:1725678] 还剩11张票 - {number = 4, name = (null)}
2018-07-22 22:26:23.155883+0800 04-线程同步[29857:1725855] 取20,还剩160元 - {number = 7, name = (null)}
2018-07-22 22:26:23.155920+0800 04-线程同步[29857:1725848] 还剩10张票 - {number = 6, name = (null)}
2018-07-22 22:26:23.156836+0800 04-线程同步[29857:1725854] 存50,还剩210元 - {number = 5, name = (null)}
2018-07-22 22:26:23.157365+0800 04-线程同步[29857:1725853] 还剩9张票 - {number = 8, name = (null)}
2018-07-22 22:26:23.157657+0800 04-线程同步[29857:1725855] 取20,还剩190元 - {number = 7, name = (null)}
2018-07-22 22:26:23.158127+0800 04-线程同步[29857:1725678] 还剩8张票 - {number = 4, name = (null)}
2018-07-22 22:26:23.158539+0800 04-线程同步[29857:1725854] 存50,还剩240元 - {number = 5, name = (null)}
2018-07-22 22:26:23.158957+0800 04-线程同步[29857:1725848] 还剩7张票 - {number = 6, name = (null)}
2018-07-22 22:26:23.159189+0800 04-线程同步[29857:1725855] 取20,还剩220元 - {number = 7, name = (null)}
2018-07-22 22:26:23.159498+0800 04-线程同步[29857:1725853] 还剩6张票 - {number = 8, name = (null)}
2018-07-22 22:26:23.159660+0800 04-线程同步[29857:1725854] 存50,还剩270元 - {number = 5, name = (null)}
2018-07-22 22:26:23.160003+0800 04-线程同步[29857:1725678] 还剩5张票 - {number = 4, name = (null)}
2018-07-22 22:26:23.160370+0800 04-线程同步[29857:1725855] 取20,还剩250元 - {number = 7, name = (null)}
2018-07-22 22:26:23.160664+0800 04-线程同步[29857:1725848] 还剩4张票 - {number = 6, name = (null)}
2018-07-22 22:26:23.160979+0800 04-线程同步[29857:1725854] 存50,还剩300元 - {number = 5, name = (null)}
2018-07-22 22:26:23.161310+0800 04-线程同步[29857:1725853] 还剩3张票 - {number = 8, name = (null)}
2018-07-22 22:26:23.161561+0800 04-线程同步[29857:1725855] 取20,还剩280元 - {number = 7, name = (null)}
2018-07-22 22:26:23.161849+0800 04-线程同步[29857:1725678] 还剩2张票 - {number = 4, name = (null)}
2018-07-22 22:26:23.162266+0800 04-线程同步[29857:1725854] 存50,还剩330元 - {number = 5, name = (null)}
2018-07-22 22:26:23.162539+0800 04-线程同步[29857:1725848] 还剩1张票 - {number = 6, name = (null)}
2018-07-22 22:26:23.162795+0800 04-线程同步[29857:1725855] 取20,还剩310元 - {number = 7, name = (null)}
2018-07-22 22:26:23.163113+0800 04-线程同步[29857:1725853] 还剩0张票 - {number = 8, name = (null)}
2018-07-22 22:26:23.163401+0800 04-线程同步[29857:1725854] 存50,还剩360元 - {number = 5, name = (null)}
2018-07-22 22:26:23.163923+0800 04-线程同步[29857:1725855] 取20,还剩340元 - {number = 7, name = (null)}
2018-07-22 22:26:23.164072+0800 04-线程同步[29857:1725854] 存50,还剩390元 - {number = 5, name = (null)}
2018-07-22 22:26:23.164316+0800 04-线程同步[29857:1725855] 取20,还剩370元 - {number = 7, name = (null)}
2018-07-22 22:26:23.164567+0800 04-线程同步[29857:1725854] 存50,还剩420元 - {number = 5, name = (null)}
2018-07-22 22:26:23.164847+0800 04-线程同步[29857:1725855] 取20,还剩400元 - {number = 7, name = (null)}

11、dispatch_semaphore

  • semaphore叫做信号量
  • 信号量的初始值,可以用来控制线程并发访问的最大数量
  • 信号量的初始值为1,代表同时只允许1条线程访问资源,保证线程同步
  • 测试代码

#import "ViewController.h"

@interface ViewController ()

@property (assign, nonatomic) int ticketsCount;
@property (assign, nonatomic) int money;

@property (strong, nonatomic) dispatch_semaphore_t ticketSemaphore;
@property (strong, nonatomic) dispatch_semaphore_t moneySemaphore;

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    
    self.ticketSemaphore = dispatch_semaphore_create(1);
    self.moneySemaphore = dispatch_semaphore_create(1);

}


- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
    [self ticketTest];
    [self moneyTest];
}

/**
 卖票演示
 */
- (void)ticketTest
{
    // 15张票
    self.ticketsCount = 15;
    
    dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
    // 窗口1
    dispatch_async(queue, ^{
        for (int i = 0; i < 5; i++) {
            [self saleTicket];
        }
    });
    // 窗口2
    dispatch_async(queue, ^{
        for (int i = 0; i < 5; i++) {
            [self saleTicket];
        }
    });
    // 窗口3
    dispatch_async(queue, ^{
        for (int i = 0; i < 5; i++) {
            [self saleTicket];
        }
    });
}

/**
 卖1张票
 */
- (void)saleTicket {
    dispatch_semaphore_wait(self.ticketSemaphore, DISPATCH_TIME_FOREVER);
        int oldTicketsCount = self.ticketsCount;
        sleep(.2);
        oldTicketsCount--;
        self.ticketsCount = oldTicketsCount;
        NSLog(@"还剩%d张票 - %@", oldTicketsCount, [NSThread currentThread]);
    dispatch_semaphore_signal(self.ticketSemaphore);
}

/**
 存钱、取钱演示
 */
- (void)moneyTest {
    //初始账户余额有100
    self.money = 100;
    //全局队列
    dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
    //多个线程存钱
    dispatch_async(queue, ^{
        for (int i = 0; i < 10; i++) {
            [self saveMoney];
        }
    });
    
    //多个线程取钱
    dispatch_async(queue, ^{
        for (int i = 0; i < 10; i++) {
            [self drawMoney];
        }
    });
}

/**
 存钱
 */
- (void)saveMoney {
    dispatch_semaphore_wait(self.moneySemaphore, DISPATCH_TIME_FOREVER);
        int oldMoney = self.money;
        sleep(.2);
        oldMoney += 50;
        self.money = oldMoney;
        NSLog(@"存50,还剩%d元 - %@", oldMoney, [NSThread currentThread]);
    dispatch_semaphore_signal(self.moneySemaphore);
}

/**
 取钱
 */
- (void)drawMoney {
    dispatch_semaphore_wait(self.moneySemaphore, DISPATCH_TIME_FOREVER);
        int oldMoney = self.money;
        sleep(.2);
        oldMoney -= 20;
        self.money = oldMoney;
        NSLog(@"取20,还剩%d元 - %@", oldMoney, [NSThread currentThread]);
    dispatch_semaphore_signal(self.moneySemaphore);
}

@end
  • 打印输出
2018-07-22 22:34:30.636008+0800 04-线程同步[29977:1732857] 存50,还剩150元 - {number = 5, name = (null)}
2018-07-22 22:34:30.636035+0800 04-线程同步[29977:1732856] 还剩14张票 - {number = 4, name = (null)}
2018-07-22 22:34:30.636316+0800 04-线程同步[29977:1732855] 取20,还剩130元 - {number = 6, name = (null)}
2018-07-22 22:34:30.636333+0800 04-线程同步[29977:1732854] 还剩13张票 - {number = 7, name = (null)}
2018-07-22 22:34:30.636481+0800 04-线程同步[29977:1732857] 存50,还剩180元 - {number = 5, name = (null)}
2018-07-22 22:34:30.636627+0800 04-线程同步[29977:1732855] 取20,还剩160元 - {number = 6, name = (null)}
2018-07-22 22:34:30.636627+0800 04-线程同步[29977:1732858] 还剩12张票 - {number = 3, name = (null)}
2018-07-22 22:34:30.636836+0800 04-线程同步[29977:1732856] 还剩11张票 - {number = 4, name = (null)}
2018-07-22 22:34:30.636839+0800 04-线程同步[29977:1732857] 存50,还剩210元 - {number = 5, name = (null)}
2018-07-22 22:34:30.638291+0800 04-线程同步[29977:1732854] 还剩10张票 - {number = 7, name = (null)}
2018-07-22 22:34:30.639155+0800 04-线程同步[29977:1732855] 取20,还剩190元 - {number = 6, name = (null)}
2018-07-22 22:34:30.639900+0800 04-线程同步[29977:1732858] 还剩9张票 - {number = 3, name = (null)}
2018-07-22 22:34:30.640501+0800 04-线程同步[29977:1732857] 存50,还剩240元 - {number = 5, name = (null)}
2018-07-22 22:34:30.640812+0800 04-线程同步[29977:1732856] 还剩8张票 - {number = 4, name = (null)}
2018-07-22 22:34:30.641218+0800 04-线程同步[29977:1732855] 取20,还剩220元 - {number = 6, name = (null)}
2018-07-22 22:34:30.641638+0800 04-线程同步[29977:1732854] 还剩7张票 - {number = 7, name = (null)}
2018-07-22 22:34:30.642011+0800 04-线程同步[29977:1732857] 存50,还剩270元 - {number = 5, name = (null)}
2018-07-22 22:34:30.642609+0800 04-线程同步[29977:1732858] 还剩6张票 - {number = 3, name = (null)}
2018-07-22 22:34:30.642842+0800 04-线程同步[29977:1732855] 取20,还剩250元 - {number = 6, name = (null)}
2018-07-22 22:34:30.643047+0800 04-线程同步[29977:1732856] 还剩5张票 - {number = 4, name = (null)}
2018-07-22 22:34:30.643281+0800 04-线程同步[29977:1732857] 存50,还剩300元 - {number = 5, name = (null)}
2018-07-22 22:34:30.643527+0800 04-线程同步[29977:1732854] 还剩4张票 - {number = 7, name = (null)}
2018-07-22 22:34:30.643775+0800 04-线程同步[29977:1732855] 取20,还剩280元 - {number = 6, name = (null)}
2018-07-22 22:34:30.644065+0800 04-线程同步[29977:1732858] 还剩3张票 - {number = 3, name = (null)}
2018-07-22 22:34:30.644298+0800 04-线程同步[29977:1732857] 存50,还剩330元 - {number = 5, name = (null)}
2018-07-22 22:34:30.644835+0800 04-线程同步[29977:1732856] 还剩2张票 - {number = 4, name = (null)}
2018-07-22 22:34:30.645167+0800 04-线程同步[29977:1732855] 取20,还剩310元 - {number = 6, name = (null)}
2018-07-22 22:34:30.645469+0800 04-线程同步[29977:1732854] 还剩1张票 - {number = 7, name = (null)}
2018-07-22 22:34:30.645743+0800 04-线程同步[29977:1732857] 存50,还剩360元 - {number = 5, name = (null)}
2018-07-22 22:34:30.646028+0800 04-线程同步[29977:1732858] 还剩0张票 - {number = 3, name = (null)}
2018-07-22 22:34:30.646373+0800 04-线程同步[29977:1732855] 取20,还剩340元 - {number = 6, name = (null)}
2018-07-22 22:34:30.646989+0800 04-线程同步[29977:1732857] 存50,还剩390元 - {number = 5, name = (null)}
2018-07-22 22:34:30.647226+0800 04-线程同步[29977:1732855] 取20,还剩370元 - {number = 6, name = (null)}
2018-07-22 22:34:30.647470+0800 04-线程同步[29977:1732857] 存50,还剩420元 - {number = 5, name = (null)}
2018-07-22 22:34:30.647797+0800 04-线程同步[29977:1732855] 取20,还剩400元 - {number = 6, name = (null)}

12、@synchronized

  • @synchronized是对mutex递归锁的封装
  • @synchronized(obj)内部生成obj对应的递归锁,然后进行加锁、解锁操作
  • 测试代码

#import "ViewController.h"

@interface ViewController ()

@property (assign, nonatomic) int ticketsCount;
@property (assign, nonatomic) int money;


@end

@implementation ViewController

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
    [self ticketTest];
    [self moneyTest];
}

/**
 卖票演示
 */
- (void)ticketTest
{
    // 15张票
    self.ticketsCount = 15;
    
    dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
    // 窗口1
    dispatch_async(queue, ^{
        for (int i = 0; i < 5; i++) {
            [self saleTicket];
        }
    });
    // 窗口2
    dispatch_async(queue, ^{
        for (int i = 0; i < 5; i++) {
            [self saleTicket];
        }
    });
    // 窗口3
    dispatch_async(queue, ^{
        for (int i = 0; i < 5; i++) {
            [self saleTicket];
        }
    });
}

/**
 卖1张票
 */
- (void)saleTicket {
    static NSString *ticketLock;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        ticketLock = [[NSString alloc] init];
    });
    
    @synchronized(ticketLock) {
        int oldTicketsCount = self.ticketsCount;
        sleep(.2);
        oldTicketsCount--;
        self.ticketsCount = oldTicketsCount;
        NSLog(@"还剩%d张票 - %@", oldTicketsCount, [NSThread currentThread]);
    }
}

/**
 存钱、取钱演示
 */
- (void)moneyTest {
    //初始账户余额有100
    self.money = 100;
    //全局队列
    dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
    //多个线程存钱
    dispatch_async(queue, ^{
        for (int i = 0; i < 10; i++) {
            [self saveMoney];
        }
    });
    
    //多个线程取钱
    dispatch_async(queue, ^{
        for (int i = 0; i < 10; i++) {
            [self drawMoney];
        }
    });
}

/**
 存钱
 */
- (void)saveMoney {
    @synchronized(self) {
        int oldMoney = self.money;
        sleep(.2);
        oldMoney += 50;
        self.money = oldMoney;
        NSLog(@"存50,还剩%d元 - %@", oldMoney, [NSThread currentThread]);
    }
}

/**
 取钱
 */
- (void)drawMoney {
    @synchronized(self) {
        int oldMoney = self.money;
        sleep(.2);
        oldMoney -= 20;
        self.money = oldMoney;
        NSLog(@"取20,还剩%d元 - %@", oldMoney, [NSThread currentThread]);
    }
}

@end
  • 打印输出
2018-07-22 22:43:42.838832+0800 04-线程同步[30110:1740326] 还剩14张票 - {number = 4, name = (null)}
2018-07-22 22:43:42.838959+0800 04-线程同步[30110:1740474] 存50,还剩150元 - {number = 5, name = (null)}
2018-07-22 22:43:42.839288+0800 04-线程同步[30110:1740473] 还剩13张票 - {number = 6, name = (null)}
2018-07-22 22:43:42.839556+0800 04-线程同步[30110:1740475] 取20,还剩130元 - {number = 7, name = (null)}
2018-07-22 22:43:42.839642+0800 04-线程同步[30110:1740471] 还剩12张票 - {number = 8, name = (null)}
2018-07-22 22:43:42.839782+0800 04-线程同步[30110:1740474] 存50,还剩180元 - {number = 5, name = (null)}
2018-07-22 22:43:42.839905+0800 04-线程同步[30110:1740326] 还剩11张票 - {number = 4, name = (null)}
2018-07-22 22:43:42.840688+0800 04-线程同步[30110:1740475] 取20,还剩160元 - {number = 7, name = (null)}
2018-07-22 22:43:42.840737+0800 04-线程同步[30110:1740473] 还剩10张票 - {number = 6, name = (null)}
2018-07-22 22:43:42.842021+0800 04-线程同步[30110:1740474] 存50,还剩210元 - {number = 5, name = (null)}
2018-07-22 22:43:42.843026+0800 04-线程同步[30110:1740471] 还剩9张票 - {number = 8, name = (null)}
2018-07-22 22:43:42.843895+0800 04-线程同步[30110:1740475] 取20,还剩190元 - {number = 7, name = (null)}
2018-07-22 22:43:42.845141+0800 04-线程同步[30110:1740326] 还剩8张票 - {number = 4, name = (null)}
2018-07-22 22:43:42.845141+0800 04-线程同步[30110:1740474] 存50,还剩240元 - {number = 5, name = (null)}
2018-07-22 22:43:42.845505+0800 04-线程同步[30110:1740475] 取20,还剩220元 - {number = 7, name = (null)}
2018-07-22 22:43:42.845989+0800 04-线程同步[30110:1740473] 还剩7张票 - {number = 6, name = (null)}
2018-07-22 22:43:42.846428+0800 04-线程同步[30110:1740474] 存50,还剩270元 - {number = 5, name = (null)}
2018-07-22 22:43:42.847317+0800 04-线程同步[30110:1740471] 还剩6张票 - {number = 8, name = (null)}
2018-07-22 22:43:42.847539+0800 04-线程同步[30110:1740475] 取20,还剩250元 - {number = 7, name = (null)}
2018-07-22 22:43:42.847753+0800 04-线程同步[30110:1740326] 还剩5张票 - {number = 4, name = (null)}
2018-07-22 22:43:42.847939+0800 04-线程同步[30110:1740474] 存50,还剩300元 - {number = 5, name = (null)}
2018-07-22 22:43:42.848174+0800 04-线程同步[30110:1740473] 还剩4张票 - {number = 6, name = (null)}
2018-07-22 22:43:42.848437+0800 04-线程同步[30110:1740475] 取20,还剩280元 - {number = 7, name = (null)}
2018-07-22 22:43:42.848697+0800 04-线程同步[30110:1740471] 还剩3张票 - {number = 8, name = (null)}
2018-07-22 22:43:42.849337+0800 04-线程同步[30110:1740474] 存50,还剩330元 - {number = 5, name = (null)}
2018-07-22 22:43:42.849484+0800 04-线程同步[30110:1740326] 还剩2张票 - {number = 4, name = (null)}
2018-07-22 22:43:42.849774+0800 04-线程同步[30110:1740475] 取20,还剩310元 - {number = 7, name = (null)}
2018-07-22 22:43:42.849942+0800 04-线程同步[30110:1740473] 还剩1张票 - {number = 6, name = (null)}
2018-07-22 22:43:42.850299+0800 04-线程同步[30110:1740474] 存50,还剩360元 - {number = 5, name = (null)}
2018-07-22 22:43:42.850579+0800 04-线程同步[30110:1740471] 还剩0张票 - {number = 8, name = (null)}
2018-07-22 22:43:42.850868+0800 04-线程同步[30110:1740475] 取20,还剩340元 - {number = 7, name = (null)}
2018-07-22 22:43:42.851388+0800 04-线程同步[30110:1740474] 存50,还剩390元 - {number = 5, name = (null)}
2018-07-22 22:43:42.851601+0800 04-线程同步[30110:1740475] 取20,还剩370元 - {number = 7, name = (null)}
2018-07-22 22:43:42.851942+0800 04-线程同步[30110:1740474] 存50,还剩420元 - {number = 5, name = (null)}
2018-07-22 22:43:42.852291+0800 04-线程同步[30110:1740475] 取20,还剩400元 - {number = 7, name = (null)}

三、自旋锁、互斥锁比较

  • 什么情况选择自旋锁
    • 预计线程等待锁的时间段
    • 加锁的代码(临界区)经常被调用,但竞争情况很少发送
    • CPU资源不紧张
    • 多核处理器
  • 什么情况选择互斥锁
    • 预计线程等待锁的时间较长
    • 单核处理器
    • 临界区有IO操作
    • 临界区代码复杂或者循环量大
    • 临界区竞争非常激烈

四、iOS中读写安全方案

  • 场景
    • 同一时间,只能有一个线程进行写的操作
    • 同一时间,允许有多个线程进行读的操作
    • 同一时间,不允许既有写的操作,又有读的操作
    • 这个场景就是典型的“多读单写”,经常用于文件等数据的读写操作,iOS中实现的方案有
    • pthread_rwlock:读写锁
    • dispatch_barrier_async:异步栅栏调用

1、pthread_rwlock读写锁

  • 等待锁的线程会进入休眠
  • 测试代码

#import "ViewController.h"
#import 


@interface ViewController ()

@property (nonatomic, assign) pthread_rwlock_t rwLock;

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    pthread_rwlock_init(&_rwLock, NULL);
}

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
    for (int i = 0; i < 5; i ++) {
        [[[NSThread alloc] initWithTarget:self selector:@selector(read) object:nil] start];
        [[[NSThread alloc] initWithTarget:self selector:@selector(write) object:nil] start];
    }
}

- (void)read {
    pthread_rwlock_rdlock(&_rwLock);
    sleep(1);
    NSLog(@"read");
    pthread_rwlock_unlock(&_rwLock);
}

- (void)write
{
    pthread_rwlock_wrlock(&_rwLock);
    sleep(1);
    NSLog(@"write");
    pthread_rwlock_unlock(&_rwLock);
}


@end

  • 打印输出
2018-07-22 23:10:45.701721+0800 04-线程同步[30412:1760322] read
2018-07-22 23:10:46.706434+0800 04-线程同步[30412:1760323] write
2018-07-22 23:10:47.710157+0800 04-线程同步[30412:1760324] read
2018-07-22 23:10:48.712415+0800 04-线程同步[30412:1760325] write
2018-07-22 23:10:49.715649+0800 04-线程同步[30412:1760326] read
2018-07-22 23:10:49.715695+0800 04-线程同步[30412:1760328] read
2018-07-22 23:10:50.721019+0800 04-线程同步[30412:1760327] write
2018-07-22 23:10:51.723056+0800 04-线程同步[30412:1760329] write
2018-07-22 23:10:52.728043+0800 04-线程同步[30412:1760331] write
2018-07-22 23:10:53.733762+0800 04-线程同步[30412:1760330] read
  • 分析:从打印输出发现,write的输出都是一条一条输出的,但是read可以在同一时间输出多条

2、dispatch_barrier_async

  • 这个函数传入的并发队列必须是自己通过dispatch_queue_cretate创建的
  • 如果传入的是一个串行或是一个全局并发队列,那这个函数便等同于dispatch_async函数的效果
  • 测试代码
#import "ViewController.h"
#import 

@interface ViewController ()
@property (strong, nonatomic) dispatch_queue_t queue;
@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    
//    NSOperationQueue *queue = [[NSOperationQueue alloc] init];
//    queue.maxConcurrentOperationCount = 5;
    
//    dispatch_semaphore_create(5);
    
    self.queue = dispatch_queue_create("rw_queue", DISPATCH_QUEUE_CONCURRENT);
    
    for (int i = 0; i < 5; i++) {
        dispatch_async(self.queue, ^{
            [self read];
        });
        
        dispatch_async(self.queue, ^{
            [self read];
        });
        
        dispatch_async(self.queue, ^{
            [self read];
        });
        
        dispatch_barrier_async(self.queue, ^{
            [self write];
        });
        
    }
}


- (void)read {
    sleep(1);
    NSLog(@"read");
}

- (void)write
{
    sleep(1);
    NSLog(@"write");
}

@end
  • 打印输出
2018-07-22 23:17:40.481328+0800 Interview02-读写安全[30535:1766928] read
2018-07-22 23:17:40.481342+0800 Interview02-读写安全[30535:1766927] read
2018-07-22 23:17:40.481328+0800 Interview02-读写安全[30535:1766930] read
2018-07-22 23:17:41.484770+0800 Interview02-读写安全[30535:1766930] write
2018-07-22 23:17:42.490197+0800 Interview02-读写安全[30535:1766930] read
2018-07-22 23:17:42.490192+0800 Interview02-读写安全[30535:1766928] read
2018-07-22 23:17:42.490192+0800 Interview02-读写安全[30535:1766927] read
2018-07-22 23:17:43.493596+0800 Interview02-读写安全[30535:1766927] write
2018-07-22 23:17:44.498015+0800 Interview02-读写安全[30535:1766927] read
2018-07-22 23:17:44.498018+0800 Interview02-读写安全[30535:1766930] read
2018-07-22 23:17:44.498015+0800 Interview02-读写安全[30535:1766928] read
2018-07-22 23:17:45.502841+0800 Interview02-读写安全[30535:1766928] write
2018-07-22 23:17:46.508430+0800 Interview02-读写安全[30535:1766928] read
2018-07-22 23:17:46.508432+0800 Interview02-读写安全[30535:1766930] read
2018-07-22 23:17:46.508503+0800 Interview02-读写安全[30535:1766927] read
2018-07-22 23:17:47.510766+0800 Interview02-读写安全[30535:1766927] write
2018-07-22 23:17:48.513853+0800 Interview02-读写安全[30535:1766927] read
2018-07-22 23:17:48.513855+0800 Interview02-读写安全[30535:1766930] read
2018-07-22 23:17:48.513853+0800 Interview02-读写安全[30535:1766928] read
2018-07-22 23:17:49.517524+0800 Interview02-读写安全[30535:1766930] write
  • 可以实现同一时间多个读取,但是只有一个写入操作

你可能感兴趣的:(多线程(二)线程同步)