什么是多線程
多線程:顧名思義就是多條線程同時存在,在實(shí)際開發(fā)中是非常重要的。
要了解多線程,我們首先要了解的是進(jìn)程。
什么是進(jìn)程
簡單說進(jìn)程就是我們運(yùn)行中的程序,運(yùn)行中的程序?qū)?yīng)相應(yīng)的進(jìn)程,每個程序都有一個進(jìn)程來對應(yīng),那么程序(進(jìn)程)是怎么執(zhí)行的呢。那就要談到線程了。
什么是線程,與進(jìn)程有什么關(guān)系呢
進(jìn)程是執(zhí)行程序是靠線程來執(zhí)行的,進(jìn)程與線程的關(guān)系就類似工長的車間與流水線,
每個進(jìn)程的都要靠一個或多個流水線來完成。那么為什么要使用多線程呢
多線程優(yōu)點(diǎn)缺點(diǎn)
優(yōu)點(diǎn)
- 適當(dāng)?shù)奶岣叱绦虻膱?zhí)行效率(多個線程同時執(zhí)行)
- 適當(dāng)?shù)奶岣吡速Y源利用率(cpu,內(nèi)存)
多線程缺點(diǎn)
- 占用一定的內(nèi)存空間
- 線程越多cpu的調(diào)度開銷越大
- 程序的復(fù)雜度會上升(會涉及到多線程的通信,多線程共用一個資源)
既然有多線程這個東西,我們就像這東西究竟在實(shí)際應(yīng)用中是什么樣的呢!
ios中實(shí)際應(yīng)用
主線程
我們了解應(yīng)用首先要知道線程有哪些種類
主線程
- 程序一啟動就會創(chuàng)建的線程(主線程,UI線程)
- 主要是更新UI
- 處理UI事件
主線程注意事項
- 費(fèi)時操作要放在子線程中否則會阻塞線程
子線程
處理耗時操作
插-多線程原理
多線程時間上是cpu在多條線程上快速切換執(zhí)行,造成了給我們感覺上是同時執(zhí)行
通過上面的介紹我們知道了子線程的重要性,但是我們怎么創(chuàng)建子線程呢?有多少中方法呢?

上圖介紹了現(xiàn)今各種多線程技術(shù),通過上面這些類我們就能夠創(chuàng)建線程
Pthread
現(xiàn)在使用較少,可以看出它使用的是c語言,一般我們使用的方法是[Pthread currentThread];
創(chuàng)建方法
- (IBAction)testButton:(UIButton *)sender {
//初始化一個線程
pthread_t thread;
//下方法是在線程中執(zhí)行相應(yīng)的函數(shù)
//第一個參數(shù)傳遞的時地址,傳遞地址才能在函數(shù)中更改這個對象
//第三個是指向函數(shù)的指針
pthread_create(&thread, NULL, run, NULL);
}
//創(chuàng)建一個函數(shù)
void * run(void *param)
{
//耗時操作
for (int i = 1; i++; i>1) {
NSLog(@"i= %@",[NSThread currentThread]);
}
return NULL;
}
那么我們就可以將函數(shù)操作放在函數(shù)中,那么這個函數(shù)就會在子線程中執(zhí)行了
NSthread
一共有三種創(chuàng)建線程的方式
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
[self createThread1];
}
- (void)createThread3
{
[self performSelectorInBackground:@selector(run:) withObject:@"rose"];
}
- (void)createThread2
{
//創(chuàng)建線程并開啟
[NSThread detachNewThreadSelector:@selector(run:) toTarget:self withObject:@"jack"];
}
- (void)createThread1
{
//通過Nsthread創(chuàng)建一個對象
CJNSthread *thread = [[CJNSthread alloc]initWithTarget:self selector:@selector(run:) object:@"jack"];
//開啟線程,(就會執(zhí)行線程中的方法)
[thread start];
//常用方法
thread.name = @"haha";
}
- (void)run:(NSString *)parm
{
for (NSInteger i = 0; i < 100; i++) {
NSLog(@"%@",[NSThread currentThread ]);
}
}
以上三種方式都可以創(chuàng)建線程
三種方式對比
第二三中方式創(chuàng)建線程比較簡單,直接創(chuàng)建,自動執(zhí)行,但是相對第一種就不能拿到線程不能對線程進(jìn)行更詳細(xì)的操作。
三種線程銷毀時間,我們使用第一種方式創(chuàng)建線程并且自定義一個類繼承自NSThread,重寫delloc方法,可以看到在方法調(diào)用完畢自動被類銷毀,所以創(chuàng)建是我們要手動的,銷毀是自動的。
線程狀態(tài)
線程從開始到結(jié)束都有什么狀態(tài)呢
CJNSthread *thread = [[CJNSthread alloc]initWithTarget:self selector:@selector(run:) object:@"jack"];
上面代碼創(chuàng)建線程-新建線程
[thread start];
上面代碼開啟線程- 就緒狀態(tài)
cpu調(diào)度-運(yùn)行狀態(tài)
如果阻塞線程-阻塞狀態(tài)
線程執(zhí)行完畢-進(jìn)入死亡狀態(tài)
阻塞線程代碼
+ (void)sleepUnitlDate:(NSDate *)date;
+ (void)sleepTimeInterval:(NSTimeinterval)ti;
使用多線程的隱患
多條線程同時訪問相同資源進(jìn)行操作,很容易造成數(shù)據(jù)錯亂。
解決方案:互斥鎖-訪問數(shù)據(jù)的線程增加一把鎖,線程結(jié)束之后再解鎖,其他線程才能訪問這個資源
@interface ViewController ()
//創(chuàng)建三個售票員
@property (nonatomic , strong)NSThread *thread01;
@property (nonatomic , strong)NSThread *thread02;
@property (nonatomic , strong)NSThread *thread03;
@property (nonatomic , assign)NSInteger ticketCount;
//創(chuàng)建所對象,必須是相同的鎖對象才能夠鎖住
@property (nonatomic , strong)NSObject *obj;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.thread01 = [[NSThread alloc]initWithTarget:self selector:@selector(saleTicket) object:nil];
self.thread01.name = @"售票員01";
self.thread02 = [[NSThread alloc]initWithTarget:self selector:@selector(saleTicket) object:nil];
self.thread02.name = @"售票員02";
self.thread03 = [[NSThread alloc]initWithTarget:self selector:@selector(saleTicket) object:nil];
self.thread03.name = @"售票員03";
self.ticketCount = 100;
self.obj = [[NSObject alloc]init];
}
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
[self.thread01 start];
[self.thread02 start];
[self.thread03 start];
}
- (void)saleTicket
{
while (1) {
//增加線程鎖,在取出資源之前
@synchronized(self.obj){
NSInteger count = self.ticketCount;
if (count > 0) {
//為了看得更加明顯,我們將線程睡眠
[NSThread sleepForTimeInterval:0.1];
self.ticketCount = count - 1;
NSLog(@"%@票已經(jīng)賣完,還剩%zd張",[NSThread currentThread].name,self.ticketCount);
}else
{
NSLog(@"票已經(jīng)賣完了");
break;
}
}
}
}
互斥鎖(線程同步技術(shù))
缺點(diǎn):互斥所是很耗費(fèi)cpu資源的
使用:多個線程涉及數(shù)據(jù)的訪問
線程同步:多條線程在同一條線程上執(zhí)行。
相關(guān):atomic 和nonatomic 原子性和非原子性
使用atomic:定義屬性的時候,屬性的set方法就會加鎖,會非常耗盡性能(因為屬性使用頻繁)
建議:所以使用的時候我們一般都直接定義nonatomic,個別需要的時候我們再使用atomic
線程通信
我們在開啟子線程執(zhí)行耗時操作,那么如何我們需要執(zhí)行操作后立即就更新到界面上,我們就有了回到主線程更新界面的需求,所以我們要學(xué)會線程通信。
在子線程中操作
[self performSelectorInBackground: withObject:l];
回到住線程中操作
[self performSelectorOnMainThread:]
//waitUnitilDone是否等待線程執(zhí)行完畢后繼續(xù)執(zhí)行
[self.imageView performSelector: onThread:[NSThread mainThread] withObject:image waitUntilDone:NO];