線程同步方案
- OSSpinLock 自旋鎖
- os_unfair_lock
- pthread_mutex
- dispatch_semaphore 信號(hào)量
- dispatch_queue(DISPATCH_QUEUE_SERIAL) 串行隊(duì)列
- NSLock
- NSRecursiveLock
- NSCondition
- NSConditionLock 條件鎖
- @synchronized
賣票方法
#import "ViewController.h"
#import <libkern/OSAtomic.h> //導(dǎo)入頭文件 自旋鎖
@interface ViewController ()
@property (assign, nonatomic) OSSpinLock lock;
@end
/**
賣票演示
*/
- (void)ticketTest
{
self.ticketsCount = 15;
dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
dispatch_async(queue, ^{
for (int i = 0; i < 5; i++) {
[self saleTicket];
}
});
dispatch_async(queue, ^{
for (int i = 0; i < 5; i++) {
[self saleTicket];
}
});
dispatch_async(queue, ^{
for (int i = 0; i < 5; i++) {
[self saleTicket];
}
});
}
/**
賣1張票
*/
- (void)saleTicket
{
// 加鎖
OSSpinLockLock(&_lock);
int oldTicketsCount = self.ticketsCount;
sleep(.2);
oldTicketsCount--;
self.ticketsCount = oldTicketsCount;
NSLog(@"還剩%d張票 - %@", oldTicketsCount, [NSThread currentThread]);
// 解鎖
OSSpinLockUnlock(&_lock);
//嘗試加鎖 更加安全
// if (OSSpinLockTry(&_lock)) {
// int oldTicketsCount = self.ticketsCount;
// sleep(.2);
// oldTicketsCount--;
// self.ticketsCount = oldTicketsCount;
// NSLog(@"還剩%d張票 - %@", oldTicketsCount, [NSThread currentThread]);
//
// OSSpinLockUnlock(&_lock);
// }
}
2018-09-12 09:49:28.048945+0800 Interview04-線程同步[986:58920] 還剩14張票 - <NSThread: 0x60400027d200>{number = 3, name = (null)}
2018-09-12 09:49:28.049229+0800 Interview04-線程同步[986:58920] 還剩13張票 - <NSThread: 0x60400027d200>{number = 3, name = (null)}
2018-09-12 09:49:28.049457+0800 Interview04-線程同步[986:58920] 還剩12張票 - <NSThread: 0x60400027d200>{number = 3, name = (null)}
2018-09-12 09:49:28.050802+0800 Interview04-線程同步[986:58920] 還剩11張票 - <NSThread: 0x60400027d200>{number = 3, name = (null)}
2018-09-12 09:49:28.050978+0800 Interview04-線程同步[986:58920] 還剩10張票 - <NSThread: 0x60400027d200>{number = 3, name = (null)}
2018-09-12 09:49:28.051562+0800 Interview04-線程同步[986:58917] 還剩9張票 - <NSThread: 0x60400027d780>{number = 4, name = (null)}
2018-09-12 09:49:28.051847+0800 Interview04-線程同步[986:58917] 還剩8張票 - <NSThread: 0x60400027d780>{number = 4, name = (null)}
2018-09-12 09:49:28.052481+0800 Interview04-線程同步[986:58917] 還剩7張票 - <NSThread: 0x60400027d780>{number = 4, name = (null)}
2018-09-12 09:49:28.052850+0800 Interview04-線程同步[986:58917] 還剩6張票 - <NSThread: 0x60400027d780>{number = 4, name = (null)}
2018-09-12 09:49:28.053069+0800 Interview04-線程同步[986:58917] 還剩5張票 - <NSThread: 0x60400027d780>{number = 4, name = (null)}
2018-09-12 09:49:28.194761+0800 Interview04-線程同步[986:58919] 還剩4張票 - <NSThread: 0x604000276140>{number = 5, name = (null)}
2018-09-12 09:49:28.195136+0800 Interview04-線程同步[986:58919] 還剩3張票 - <NSThread: 0x604000276140>{number = 5, name = (null)}
2018-09-12 09:49:28.195393+0800 Interview04-線程同步[986:58919] 還剩2張票 - <NSThread: 0x604000276140>{number = 5, name = (null)}
2018-09-12 09:49:28.195626+0800 Interview04-線程同步[986:58919] 還剩1張票 - <NSThread: 0x604000276140>{number = 5, name = (null)}
2018-09-12 09:49:28.195803+0800 Interview04-線程同步[986:58919] 還剩0張票 - <NSThread: 0x604000276140>{number = 5, name = (null)}
OSSpinLock總結(jié)
OSSpinLock叫做”自旋鎖”,等待鎖的線程會(huì)處于忙等(busy-wait)狀態(tài),一直占用著CPU資源
目前已經(jīng)不再安全,可能會(huì)出現(xiàn)優(yōu)先級(jí)反轉(zhuǎn)問(wèn)題
不安全,會(huì)根據(jù)優(yōu)先級(jí)反轉(zhuǎn),優(yōu)先級(jí)高的會(huì)先加鎖!
優(yōu)先級(jí)反轉(zhuǎn) 如果線程2先進(jìn)去,線程1的優(yōu)先級(jí)比較高,那么CPU會(huì)更線程1分配大量資源,導(dǎo)致線程2資源很好,然后線程2就無(wú)法解鎖,會(huì)導(dǎo)致死鎖現(xiàn)象
os_unfair_lock
- os_unfair_lock用于取代不安全的OSSpinLock ,從iOS10開(kāi)始才支持
- 從底層調(diào)用看,等待os_unfair_lock鎖的線程會(huì)處于休眠狀態(tài),并非忙等
- 需要導(dǎo)入頭文件#import <os/lock.h>
os_unfair_lock lock = OS_UNFAIR_LOCK_INIT
os_unfair_lock_trylock(&lock);//嘗試加鎖
os_unfair_lock_lock(& lock);//加鎖
os_unfair_lock_unlock(& lock); //解鎖
os_unfair_lock的特點(diǎn)等不到鎖就休眠
pthread_mutex
- pthread開(kāi)頭的是垮平臺(tái)的
- mutex叫做”互斥鎖”,等待鎖的線程會(huì)處于休眠狀態(tài)
- 需要導(dǎo)入頭文件#import <pthread.h>
pthread_mutex_t mutex = PTHREAD_MUTEX_DEFAULT;
// #define PTHREAD_MUTEX_ERRORCHECK 1 //排錯(cuò)鎖
// #define PTHREAD_MUTEX_RECURSIVE 2 //遞歸鎖
// #define PTHREAD_MUTEX_DEFAULT //默認(rèn) PTHREAD_MUTEX_NORMAL
// 初始化屬性
pthread_mutexattr_t attr;
pthread_mutexattr_init(&attr);
pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_DEFAULT);
// 初始化鎖
pthread_mutex_init(mutex, &attr);
// 銷毀屬性
pthread_mutexattr_destroy(&attr);
- (void)ticket
{
pthread_mutex_lock(&_ticketMutex); //加鎖
執(zhí)行的代碼
pthread_mutex_unlock(&_ticketMutex); //解鎖
}
- 默認(rèn)鎖只能加一次鎖
pthread_mutex 遞歸鎖
pthread_mutex_t mutex = PTHREAD_MUTEX_RECURSIVE;
// 初始化屬性
pthread_mutexattr_t attr;
pthread_mutexattr_init(&attr);
pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_DEFAULT);
// 初始化鎖
pthread_mutex_init(mutex, &attr);
// 銷毀屬性
pthread_mutexattr_destroy(&attr);
//比如調(diào)用頻繁這樣調(diào)用遞歸
- (void)otherTest
{
pthread_mutex_lock(&_mutex);
NSLog(@"%s", __func__);
static int count = 0;
if (count < 10) {
count++;
[self otherTest];
}
pthread_mutex_unlock(&_mutex);
}
- 遞歸鎖 允許同一線程可以多次加鎖(不同線程 不允許這樣就可以保護(hù)線程)
NSLock ,NSRecursiveLock
- NSLock是對(duì)mutex普通鎖的封裝
- NSRecursiveLock也是對(duì)mutex遞歸鎖的封裝,API跟NSLock基本一致
@property (strong, nonatomic) NSLock *ticketLock;
- (instancetype)init
{
if (self = [super init]) {
self.ticketLock = [[NSLock alloc] init];
}
return self;
}
- (void)saleTicket
{
[self.ticketLock lock]; 加鎖
加鎖的內(nèi)容代碼
[self.ticketLock unlock]; 解鎖
}
NSCondition
- NSCondition是對(duì)mutex和cond的封裝
/** 系統(tǒng)提供的方法*/
- (void)wait;
- (BOOL)waitUntilDate:(NSDate *)limit;
- (void)signal;
- (void)broadcast;
@property (strong, nonatomic) NSCondition *condition;
@property (strong, nonatomic) NSMutableArray *data;
- (instancetype)init
{
if (self = [super init]) {
self.condition = [[NSCondition alloc] init];
self.data = [NSMutableArray array];
}
return self;
}
// 生產(chǎn)者-消費(fèi)者模式
// 線程1
// 刪除數(shù)組中的元素
- (void)__remove
{
[self.condition lock];
NSLog(@"__remove - begin");
if (self.data.count == 0) {
// 等待
[self.condition wait];
}
[self.data removeLastObject];
NSLog(@"刪除了元素");
[self.condition unlock];
}
// 線程2
// 往數(shù)組中添加元素
- (void)__add
{
[self.condition lock];
sleep(1);
[self.data addObject:@"Test"];
NSLog(@"添加了元素");
// 信號(hào)
[self.condition signal];
// 廣播
// [self.condition broadcast];
[self.condition unlock];
}
NSConditionLock
- NSConditionLock是對(duì)NSCondition的進(jìn)一步封裝,可以設(shè)置具體的條件值
- 條件鎖
/** 系統(tǒng)提供方法 */
- (void)lockWhenCondition:(NSInteger)condition;
- (BOOL)tryLock;
- (BOOL)tryLockWhenCondition:(NSInteger)condition;
- (void)unlockWithCondition:(NSInteger)condition;
- (BOOL)lockBeforeDate:(NSDate *)limit;
- (BOOL)lockWhenCondition:(NSInteger)condition beforeDate:(NSDate *)limit;
現(xiàn)在我想執(zhí)行__one 執(zhí)行線程 __two __three
@property (strong, nonatomic) NSConditionLock *conditionLock;
- (instancetype)init
{
if (self = [super init]) {
self.conditionLock = [[NSConditionLock alloc] initWithCondition:1];
}
return self;
}
- (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];
}
dispatch_queue(DISPATCH_QUEUE_SERIAL)
@property (strong, nonatomic) dispatch_queue_t ticketQueue;
- (instancetype)init
{
if (self = [super init]) {
self.ticketQueue = dispatch_queue_create("ticketQueue", DISPATCH_QUEUE_SERIAL);
}
return self;
}
- (void)__saleTicket
{
dispatch_sync(self.ticketQueue, ^{
代碼
});
}
dispatch_semaphore_t 信號(hào)量
- semaphore叫做”信號(hào)量”
- 信號(hào)量的初始值,可以用來(lái)控制線程并發(fā)訪問(wèn)的最大數(shù)量
- 信號(hào)量的初始值為1,代表同時(shí)只允許1條線程訪問(wèn)資源,保證線程同步
// 信號(hào)量初始值
int value = 1;
//初始化信號(hào)量
@property (strong, nonatomic) dispatch_semaphore_t ticketSemaphore;
- (instancetype)init
{
if (self = [super init]) {
self.ticketSemaphore = dispatch_semaphore_create(1);
}
return self;
}
// 線程10、7、6、9、8
- (void)test
{
// 如果信號(hào)量的值 > 0,就讓信號(hào)量的值減1,然后繼續(xù)往下執(zhí)行代碼
// 如果信號(hào)量的值 <= 0,就會(huì)休眠等待,直到信號(hào)量的值變成>0,就讓信號(hào)量的值減1,然后繼續(xù)往下執(zhí)行代碼
dispatch_semaphore_wait(self.semaphore, DISPATCH_TIME_FOREVER);
sleep(2);
NSLog(@"test - %@", [NSThread currentThread]);
// 讓信號(hào)量的值+1
dispatch_semaphore_signal(self.semaphore);
}