版權(quán)聲明:本文為博主原創(chuàng)文章焚辅,未經(jīng)博主允許不得轉(zhuǎn)載映屋。
2017/09/06
看到一篇關(guān)于線程鎖的文章,十分的精彩同蜻。有興趣的朋友可以點(diǎn)進(jìn)去看看棚点。
下面是幾點(diǎn)自己關(guān)于線程鎖的體會(huì)
什么是線程鎖?
一般情況下湾蔓,多個(gè)進(jìn)程可以同時(shí)訪問一個(gè)內(nèi)存瘫析。但如果中間穿插寫操作就會(huì)發(fā)生一些異常。所以需要用線程鎖默责,限制同一時(shí)刻只能有一個(gè)線程訪問一個(gè)內(nèi)存空間贬循。讀寫是如何的實(shí)現(xiàn)?(主要講寫)
. 首先從內(nèi)存中讀取數(shù)據(jù)到寄存器,i = 0
. 在寄存器中對(duì)數(shù)據(jù)進(jìn)行寫操作 i = 1
. 將寄存器中操作后的數(shù)據(jù)寫入內(nèi)存, i = 1
從這里可以看到如果A桃序、B進(jìn)行同時(shí)進(jìn)行杖虾,A進(jìn)行寫數(shù)據(jù),B進(jìn)行讀數(shù)據(jù)媒熊。則B讀取到的數(shù)據(jù)可能是A寫之前的數(shù)據(jù)也有可能是A寫之后的數(shù)據(jù)
- 線程鎖有哪些類型奇适?
互斥鎖、自旋鎖芦鳍、讀寫鎖
從 實(shí)現(xiàn)原理上來講,Mutex屬于sleep-waiting類型的鎖柠衅。例如在一個(gè)雙核的機(jī)器上有兩個(gè)線程(線程A和線程B)皮仁,它們分別運(yùn)行在Core0和 Core1上。
假設(shè)線程A想要通過pthread_mutex_lock操作去得到一個(gè)臨界區(qū)的鎖茄茁,而此時(shí)這個(gè)鎖正被線程B所持有魂贬,那么線程A就會(huì)被阻塞 (blocking),Core0 會(huì)在此時(shí)進(jìn)行上下文切換(Context Switch)將線程A置于等待隊(duì)列中裙顽,此時(shí)Core0就可以運(yùn)行其他的任務(wù)(例如另一個(gè)線程C)而不必進(jìn)行忙等待
結(jié)論:
- async操作開辟子線程付燥,sync操作不開辟子線程;
2 . 同步會(huì)阻塞當(dāng)前隊(duì)列添加其他線程愈犹,異步不會(huì)阻塞當(dāng)前隊(duì)列键科。 - 并發(fā)隊(duì)列可以同時(shí)執(zhí)行多個(gè)線程任務(wù),串行隊(duì)列同時(shí)只能執(zhí)行一個(gè)線程任務(wù)漩怎。
多線程
在iOS 中勋颖,操作多線程主要有4個(gè)方案:
PThreads:它是C語言的框架,使用起來很不方便勋锤,基本不會(huì)用饭玲。
NSThread:蘋果對(duì)多線程進(jìn)行了封裝,是面向?qū)ο蟮娜础5驗(yàn)樗枰覀內(nèi)ス芾砭€程的生命周期茄厘,所以用的也不是很多矮冬。不過用它去查看當(dāng)前線程,或者讓當(dāng)前線程睡眠還是很方便的次哈。
然后是GCD:GCD是蘋果對(duì)多核運(yùn)算提出的解決方案胎署,而且gcd會(huì)自己管理生命周期,這樣可以讓我們把注意力集中在具體任務(wù)上。
最后一個(gè)是是NSOperation窑滞,它是對(duì)GCD封裝琼牧。增加一些功能。比如線程都掛起和暫停哀卫,還有線程的依賴巨坊。
不過像我最常用的還GCD,對(duì)他也最了解此改。所以就講一下它吧抱究。
GCD新增的2個(gè)概念
任務(wù):
//同步為主隊(duì)列添加了一個(gè)任務(wù),等到block結(jié)束后返回結(jié)果带斑。沒有創(chuàng)建一個(gè)新的線程
dispatch_sync(dispatch_get_main_queue(), ^{
NSLog(@"2");
});
//異步為主隊(duì)列添加了一個(gè)任務(wù),立刻返回結(jié)果勋拟,創(chuàng)建了一個(gè)新的線程執(zhí)行任務(wù)
dispatch_async(dispatch_get_main_queue(), ^{
NSLog(@"2");
});
隊(duì)列
* At the core, dispatch provides serial FIFO queues to which blocks may be
* submitted. Blocks submitted to these dispatch queues are invoked on a pool
* of threads fully managed by the system. No guarantee is made regarding
* which thread a block will be invoked on; however, it is guaranteed that only
* one block submitted to the FIFO dispatch queue will be invoked at a time.
*
* When multiple queues have blocks to be processed, the system is free to
* allocate additional threads to invoke the blocks concurrently. When the
* queues become empty, these threads are automatically released.
大概意思:dispatch_sync,dispatch_async中的block代碼塊(任務(wù))會(huì)以FIFO的方式在隊(duì)列中被調(diào)用勋磕。他們將會(huì)由系統(tǒng)從線程池中隨機(jī)分配一個(gè)線程,然后執(zhí)行任務(wù)敢靡。
ps:主隊(duì)列的線程為主線程挂滓。
主隊(duì)列:dispatch_get_main_queue()
全局隊(duì)列: dispatch_get_global_queue(0, 0)
自定義隊(duì)列:
dispatch_queue_t serialQueue = dispatch_queue_create("com.lai.www", DISPATCH_QUEUE_SERIAL);//串行
dispatch_queue_t serialQueue = dispatch_queue_create("com.lai.www", DISPATCH_QUEUE_CONCURRENT);//并發(fā)
并發(fā)隊(duì)列:
- 任務(wù)以FIFO從序列中移除,然后并發(fā)運(yùn)行啸胧,一次可以調(diào)度多個(gè)赶站,可以按照任何順序完成。
- 可以同時(shí)執(zhí)行多個(gè)線程任務(wù)
串行隊(duì)列:
- 任務(wù)以FIFO從序列中一個(gè)一個(gè)執(zhí)行纺念。一次只調(diào)度一個(gè)任務(wù)
- 而且只會(huì)開啟一條線程
同步:
dispatch_queue_t Aqueue = ~;
dispatch_sync(Aqueue, ^{
NSLog(@"2");
});
* dispatch_sync() will not return until the block has finished(同步為某隊(duì)列添加了一個(gè)任務(wù)贝椿,要知道該任務(wù)返回才能返回。)
注意:
0. 不會(huì)創(chuàng)建新的線程陷谱,所以在當(dāng)前的線程中執(zhí)行
1. 該返回可以為dispatch_async() 的返回烙博,也可以為任務(wù)執(zhí)行完畢
2. 阻塞的是當(dāng)前隊(duì)列,而不是Aqueue隊(duì)列;
3. 先阻塞當(dāng)前隊(duì)列烟逊,然后在向Aqueue隊(duì)列中添加任務(wù)渣窜。(就是因?yàn)槿绱耍栽谥麝?duì)列中同步向主隊(duì)列添加任務(wù)會(huì)造成死鎖)
異步
dispatch_queue_t Aqueue = ~;
dispatch_async(Aqueue, ^{
NSLog(@"2");
});
* Calls to dispatch_async() always return immediately after the block has
* been submitted, and never wait for the block to be invoked.
(立刻返回block結(jié)果宪躯,不需要等待block中的任務(wù)被調(diào)度)
注意:
1. 會(huì)創(chuàng)建一個(gè)新的線程去執(zhí)行任務(wù)
2. 異步和同步一樣乔宿,影響的是當(dāng)前的隊(duì)列,對(duì)Aqueue隊(duì)列沒有影響访雪。
對(duì)同步異步详瑞、隊(duì)列掂林、任務(wù)的概念理解講完了,下面通過例子來更進(jìn)一步地理解蛤虐。
- (void)test2{
dispatch_queue_t serialQueue = dispatch_queue_create("com.lai.www", DISPATCH_QUEUE_SERIAL);//串行
//代碼段1
dispatch_async(serialQueue, ^{
NSLog(@"1");
sleep(3);
});
//代碼段2
for(int i = 0; i < 5; i ++){
NSLog(@"i = %d", i);
}
NSLog(@"0");
//代碼段3
dispatch_sync(serialQueue, ^{
NSLog(@"2");
sleep(1);
});
//代碼段4
NSLog(@"4");
//代碼段5
dispatch_async(serialQueue, ^{
NSLog(@"3");
});
//代碼段6
NSLog(@"5");
}
輸出結(jié)果:
2017-02-27 15:38:14.945 test[2527:158657] i = 0
2017-02-27 15:38:14.945 test[2527:158694] 1
2017-02-27 15:38:14.945 test[2527:158657] i = 1
2017-02-27 15:38:14.945 test[2527:158657] i = 2
2017-02-27 15:38:14.945 test[2527:158657] i = 3
2017-02-27 15:38:14.946 test[2527:158657] i = 4
2017-02-27 15:38:14.946 test[2527:158657] 0
2017-02-27 15:38:17.950 test[2527:158657] 2
2017-02-27 15:38:18.952 test[2527:158657] 4
2017-02-27 15:38:18.952 test[2527:158657] 5
2017-02-27 15:38:18.952 test[2527:158694] 3
推斷:serialQueue為一個(gè)串行隊(duì)列党饮,在串行隊(duì)列中的任務(wù)執(zhí)行FIFO。然后我看分析的代碼:
代碼1驳庭,異步為serialQueue添加一個(gè)任務(wù)(會(huì)開辟一個(gè)子線程執(zhí)行)刑顺,直接返回結(jié)果,所以同步執(zhí)行代碼段2饲常。這里的執(zhí)行結(jié)果返回順序(也只能看結(jié)果)會(huì)隨著任務(wù)消耗的時(shí)間來確定蹲堂。此時(shí)主線程:代碼段2;serialQueue線程:代碼段1
代碼3同步為serialQueue添加了打印2任務(wù)(不會(huì)開辟新的線程贝淤,所以在當(dāng)前的主線程中執(zhí)行)柒竞,所以會(huì)阻礙當(dāng)前隊(duì)列的其他任務(wù),主隊(duì)列無法添加其他任務(wù)播聪,等待打印2任務(wù)完成后執(zhí)行代碼4
碼段5異步在serialQueue隊(duì)列添加打印3任務(wù)朽基,所以會(huì)同時(shí)執(zhí)行代碼6 。輸出結(jié)果隨著代碼消耗時(shí)間決定离陶,這里添加到子線程中執(zhí)行的速度<執(zhí)行代碼6的速度稼虎。
列子二
打印結(jié)果:1 ,3招刨, 4霎俩, 2
// 17/2/28
解釋:
- app啟動(dòng)的時(shí)候,會(huì)默認(rèn)創(chuàng)建一個(gè)串行隊(duì)列dispatch_get_main_queue()沉眶,我們稱之為主隊(duì)列打却,在主線程中調(diào)用。當(dāng)前主隊(duì)列有1個(gè)任務(wù):打印1谎倔,異步為為全局隊(duì)列添加一個(gè)A任務(wù)柳击,打印3,4传藏。
- A任務(wù)為異步在主線程中添加一個(gè)打印任務(wù)腻暮,所以當(dāng)前主線程有2個(gè)任務(wù)(如果打印3,4沒有結(jié)束的話)毯侦。根據(jù)FIFO哭靖,輸出結(jié)果1,3侈离,4 试幽;2。
例子3:
- (void)test6{
dispatch_queue_t queue = dispatch_queue_create("金永峰", DISPATCH_QUEUE_CONCURRENT);
//代碼1
dispatch_async(queue, ^{
dispatch_sync(dispatch_get_global_queue(0, 0), ^{
for(int i = 0; i < 20; i ++){
[NSThread sleepForTimeInterval:0.5];
NSLog(@"%@++++++++, %d", [NSThread currentThread], i);
}
});
});
//代碼2
dispatch_async(dispatch_get_global_queue(0, 0), ^{
//代碼4
dispatch_sync(queue, ^{
for(int i = 0; i < 10; i ++){
[NSThread sleepForTimeInterval:0.5];
NSLog(@"%@--------%d", [NSThread currentThread],i);
}
});
});
}
打印結(jié)果:
2017-03-20 16:33:26.669 GCC[84715:2932251] <NSThread: 0x600000264a40>{number = 3, name = (null)}--------0
2017-03-20 16:33:26.669 GCC[84715:2932230] <NSThread: 0x60800007eac0>{number = 4, name = (null)}++++++++, 0
2017-03-20 16:33:27.171 GCC[84715:2932230] <NSThread: 0x60800007eac0>{number = 4, name = (null)}++++++++, 1
2017-03-20 16:33:27.171 GCC[84715:2932251] <NSThread: 0x600000264a40>{number = 3, name = (null)}--------1
2017-03-20 16:33:27.675 GCC[84715:2932230] <NSThread: 0x60800007eac0>{number = 4, name = (null)}++++++++, 2
2017-03-20 16:33:27.675 GCC[84715:2932251] <NSThread: 0x600000264a40>{number = 3, name = (null)}--------2
2017-03-20 16:33:28.178 GCC[84715:2932251] <NSThread: 0x600000264a40>{number = 3, name = (null)}--------3
推斷:
1.代碼1為異步為queue添加任務(wù)卦碾,所以直接返回block結(jié)果铺坞,可以直接執(zhí)行代碼2起宽,為全局創(chuàng)建1個(gè)任務(wù)。所以創(chuàng)建了2個(gè)任務(wù)(這一點(diǎn)要明確)济榨。
2.queu隊(duì)列里面有2個(gè)任務(wù):1. 同步創(chuàng)建一個(gè)打印任務(wù)坯沪;2:打印---;2個(gè)任務(wù)在不同的線程中擒滑,并且queue是并發(fā)對(duì)腐晾; ,所以可以同時(shí)執(zhí)行
3.全局隊(duì)列中也有2個(gè)任務(wù):1.同步創(chuàng)建1個(gè)打印任務(wù)丐一,2.打印+++藻糖;2個(gè)任務(wù)在不同的線程中,并且全局隊(duì)列是并發(fā)的库车,所以可以同時(shí)執(zhí)行
例子4:
- (void)test7{
dispatch_queue_t queue = dispatch_queue_create("金永峰", DISPATCH_QUEUE_CONCURRENT);
//代碼1
dispatch_async(queue, ^{
/** 的代碼是queue中的一個(gè)完整的任務(wù):打印\巨柒,同步在全局隊(duì)列中創(chuàng)建打印+任務(wù) */
for(int i = 0; i < 20; i ++){
[NSThread sleepForTimeInterval:0.5];
NSLog(@"%@\\\\\\", [NSThread currentThread]);
}
dispatch_sync(dispatch_get_global_queue(0, 0), ^{
for(int i = 0; i < 20; i ++){
[NSThread sleepForTimeInterval:0.5];
NSLog(@"%@++++++++, %d", [NSThread currentThread], i);
}
});
});
//代碼2
dispatch_async(dispatch_get_global_queue(0, 0), ^{
//代碼4
dispatch_sync(queue, ^{
for(int i = 0; i < 10; i ++){
[NSThread sleepForTimeInterval:0.5];
NSLog(@"%@--------%d", [NSThread currentThread],i);
}
});
});
}
推斷:
1. 代碼1為異步執(zhí)行,所以直接返回block結(jié)果柠衍,可以直接執(zhí)行代碼2洋满,所以創(chuàng)建了2個(gè)任務(wù)(這一點(diǎn)要明確,稱為A珍坊、B任務(wù))
首先:
2. queue隊(duì)列中的任務(wù)有2個(gè):1芦岂、打印\\\, 同步在全局隊(duì)列中添加任務(wù),2垫蛆、打印+++。
3. 全局隊(duì)列中的任務(wù)1個(gè):同步創(chuàng)建1個(gè)任務(wù)
所以:
* 首先腺怯,queue(并發(fā))中的執(zhí)行代碼:同時(shí)打印\\\和打印---袱饭;
* 等到打印\\\結(jié)束(此時(shí)queue開始執(zhí)行打印---任務(wù)),再同步在全局隊(duì)列中添加任務(wù)呛占;
*最后全局隊(duì)列執(zhí)行任務(wù)打印++虑乖,queue執(zhí)行任務(wù)打印--
推薦的一篇文章:并發(fā) 并行 同步 異步 多線程的區(qū)別, 多線程只是我們實(shí)現(xiàn)異步的一種手段
2017/09/26
dispatch_async(dispatch_get_main_queue(), ^{
[self logTask:@"mainQueue"];
});
dispatch_queue_t queue = dispatch_queue_create("cx", DISPATCH_QUEUE_SERIAL);
dispatch_sync(queue, ^{
[self logTask:@"sync-queue"];
});
//打印-- (模擬結(jié)果)
sync-queue
sync-queue
sync-queue
sync-queue
sync-queue
·
·
·
mainQueue
mainQueue
mainQueue
mainQueue
隊(duì)列優(yōu)先級(jí)
1. 自己創(chuàng)建的隊(duì)列優(yōu)先級(jí)要高于主隊(duì)列
出現(xiàn)以上的結(jié)果是因?yàn)?/strong>:
上面這段代碼有創(chuàng)建了2個(gè)線程任務(wù)晾虑,其中有一個(gè)是同步疹味,導(dǎo)致了主隊(duì)列的死鎖。所以第一個(gè)線程任務(wù)需要等待他的完成才能繼續(xù)執(zhí)行帜篇。
下面的例子中因?yàn)樽枞牟皇侵麝?duì)列糙捺,所以可以打印任務(wù)是同步進(jìn)行的。
dispatch_async(queue, ^{
dispatch_sync(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0), ^{
[self logTask:@"async-DISPATCH_QUEUE_PRIORITY_LOW"];
});
});
dispatch_async(dispatch_get_main_queue(), ^{
[self logTask:@"mainQueue2"];
});
//打印-- (模擬結(jié)果)
mainQueue2
DISPATCH_QUEUE_PRIORITY_LOW
DISPATCH_QUEUE_PRIORITY_LOW
mainQueue2
DISPATCH_QUEUE_PRIORITY_LOW
·
·
·
mainQueue2
DISPATCH_QUEUE_PRIORITY_LOW
DISPATCH_QUEUE_PRIORITY_LOW
mainQueue2
DISPATCH_QUEUE_PRIORITY_LOW
另外笙隙,可以看出下面2個(gè)代碼是不等價(jià)的
dispatch_async(dispatch_get_main_queue(), ^{
[self logTask:@"mainQueue2"];
});
[self logTask:@"mainQueue2"];
- 全局隊(duì)列的優(yōu)先級(jí)
DISPATCH_QUEUE_PRIORITY_HIGH >= DISPATCH_QUEUE_PRIORITY_DEFAULT > DISPATCH_QUEUE_PRIORITY_LOW > DISPATCH_QUEUE_PRIORITY_BACKGROUND
ps: 長(zhǎng)耗時(shí)任務(wù)可能不會(huì)嚴(yán)格遵循優(yōu)先級(jí)