淺析iOS應(yīng)用開發(fā)中線程間的通信與線程安全問題
線程間的通信
簡單說明
線程間通信:在1個(gè)進(jìn)程中,線程往往不是孤立存在的,多個(gè)線程之間需要經(jīng)常進(jìn)行通信
線程間通信的體現(xiàn)
1個(gè)線程傳遞數(shù)據(jù)給另1個(gè)線程
在1個(gè)線程中執(zhí)行完特定任務(wù)后,轉(zhuǎn)到另1個(gè)線程繼續(xù)執(zhí)行任務(wù)
線程間通信常用方法
- (void)performSelectorOnMainThread:(SEL)aSelector withObject:(id)arg waitUntilDone:(BOOL)wait;
- (void)performSelector:(SEL)aSelector onThread:(NSThread *)thr withObject:(id)arg waitUntilDone:(BOOL)wait;
線程間通信示例 – 圖片下載
//
// YYViewController.m
// 06-NSThread04-線程間通信
//
// Created by apple on 14-6-23.
// Copyright (c) 2014年 itcase. All rights reserved.
//
#import "YYViewController.h"
@interface YYViewController ()
@property (weak, nonatomic) IBOutlet UIImageView *iconView;
@end
@implementation YYViewController
- (void)viewDidLoad
{
[super viewDidLoad];
}
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
// 在子線程中調(diào)用download方法下載圖片
[self performSelectorInBackground:@selector(download) withObject:nil];
}
-(void)download
{
//1.根據(jù)URL下載圖片
//從網(wǎng)絡(luò)中下載圖片
NSURL *urlstr=[NSURL URLWithString:@"fdsf"];
//把圖片轉(zhuǎn)換為二進(jìn)制的數(shù)據(jù)
NSData *data=[NSData dataWithContentsOfURL:urlstr];//這一行操作會(huì)比較耗時(shí)
//把數(shù)據(jù)轉(zhuǎn)換成圖片
UIImage *image=[UIImage imageWithData:data];
//2.回到主線程中設(shè)置圖片
[self performSelectorOnMainThread:@selector(settingImage:) withObject:image waitUntilDone:NO];
}
//設(shè)置顯示圖片
-(void)settingImage:(UIImage *)image
{
self.iconView.image=image;
}
@end
代碼2:
//
// YYViewController.m
// 06-NSThread04-線程間通信
//
// Created by apple on 14-6-23.
// Copyright (c) 2014年 itcase. All rights reserved.
//
#import "YYViewController.h"
#import <NSData.h>
@interface YYViewController ()
@property (weak, nonatomic) IBOutlet UIImageView *iconView;
@end
@implementation YYViewController
- (void)viewDidLoad
{
[super viewDidLoad];
}
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
// 在子線程中調(diào)用download方法下載圖片
[self performSelectorInBackground:@selector(download) withObject:nil];
}
-(void)download
{
//1.根據(jù)URL下載圖片
//從網(wǎng)絡(luò)中下載圖片
NSURL *urlstr=[NSURL URLWithString:@"fdsf"];
//把圖片轉(zhuǎn)換為二進(jìn)制的數(shù)據(jù)
NSData *data=[NSData dataWithContentsOfURL:urlstr];//這一行操作會(huì)比較耗時(shí)
//把數(shù)據(jù)轉(zhuǎn)換成圖片
UIImage *image=[UIImage imageWithData:data];
//2.回到主線程中設(shè)置圖片
//第一種方式
// [self performSelectorOnMainThread:@selector(settingImage:) withObject:image waitUntilDone:NO];
//第二種方式
// [self.imageView performSelector:@selector(setImage:) onThread:[NSThread mainThread] withObject:image waitUntilDone:NO];
//第三種方式
[self.iconView performSelectorOnMainThread:@selector(setImage:) withObject:image waitUntilDone:NO];
}
//設(shè)置顯示圖片
//-(void)settingImage:(UIImage *)image
//{
// self.iconView.image=image;
//}
@end
線程安全
一、多線程的安全隱患
資源共享
1塊資源可能會(huì)被多個(gè)線程共享,也就是多個(gè)線程可能會(huì)訪問同一塊資源
比如多個(gè)線程訪問同一個(gè)對(duì)象、同一個(gè)變量、同一個(gè)文件
當(dāng)多個(gè)線程訪問同一塊資源時(shí),很容易引發(fā)數(shù)據(jù)錯(cuò)亂和數(shù)據(jù)安全問題
示例一:
示例二:
問題代碼:
//
// YYViewController.m
// 05-線程安全
//
// Created by apple on 14-6-23.
// Copyright (c) 2014年 itcase. All rights reserved.
//
#import "YYViewController.h"
@interface YYViewController ()
//剩余票數(shù)
@property(nonatomic,assign) int leftTicketsCount;
@property(nonatomic,strong)NSThread *thread1;
@property(nonatomic,strong)NSThread *thread2;
@property(nonatomic,strong)NSThread *thread3;
@end
@implementation YYViewController
- (void)viewDidLoad
{
[super viewDidLoad];
//默認(rèn)有20張票
self.leftTicketsCount=10;
//開啟多個(gè)線程,模擬售票員售票
self.thread1=[[NSThread alloc]initWithTarget:self selector:@selector(sellTickets) object:nil];
self.thread1.name=@"售票員A";
self.thread2=[[NSThread alloc]initWithTarget:self selector:@selector(sellTickets) object:nil];
self.thread2.name=@"售票員B";
self.thread3=[[NSThread alloc]initWithTarget:self selector:@selector(sellTickets) object:nil];
self.thread3.name=@"售票員C";
}
-(void)sellTickets
{
while (1) {
//1.先檢查票數(shù)
int count=self.leftTicketsCount;
if (count>0) {
//暫停一段時(shí)間
[NSThread sleepForTimeInterval:0.002];
//2.票數(shù)-1
self.leftTicketsCount= count-1;
//獲取當(dāng)前線程
NSThread *current=[NSThread currentThread];
NSLog(@"%@--賣了一張票,還剩余%d張票",current,self.leftTicketsCount);
}else
{
//退出線程
[NSThread exit];
}
}
}
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
//開啟線程
[self.thread1 start];
[self.thread2 start];
[self.thread3 start];
}
@end
打印結(jié)果:
二、安全隱患分析
三、如何解決
互斥鎖使用格式
@synchronized(鎖對(duì)象) { // 需要鎖定的代碼 }
注意:鎖定1份代碼只用1把鎖,用多把鎖是無效的
代碼示例:
//
// YYViewController.m
// 05-線程安全
//
// Created by apple on 14-6-23.
// Copyright (c) 2014年 itcase. All rights reserved.
//
#import "YYViewController.h"
@interface YYViewController ()
//剩余票數(shù)
@property(nonatomic,assign) int leftTicketsCount;
@property(nonatomic,strong)NSThread *thread1;
@property(nonatomic,strong)NSThread *thread2;
@property(nonatomic,strong)NSThread *thread3;
@end
@implementation YYViewController
- (void)viewDidLoad
{
[super viewDidLoad];
//默認(rèn)有20張票
self.leftTicketsCount=10;
//開啟多個(gè)線程,模擬售票員售票
self.thread1=[[NSThread alloc]initWithTarget:self selector:@selector(sellTickets) object:nil];
self.thread1.name=@"售票員A";
self.thread2=[[NSThread alloc]initWithTarget:self selector:@selector(sellTickets) object:nil];
self.thread2.name=@"售票員B";
self.thread3=[[NSThread alloc]initWithTarget:self selector:@selector(sellTickets) object:nil];
self.thread3.name=@"售票員C";
}
-(void)sellTickets
{
while (1) {
@synchronized(self){//只能加一把鎖
//1.先檢查票數(shù)
int count=self.leftTicketsCount;
if (count>0) {
//暫停一段時(shí)間
[NSThread sleepForTimeInterval:0.002];
//2.票數(shù)-1
self.leftTicketsCount= count-1;
//獲取當(dāng)前線程
NSThread *current=[NSThread currentThread];
NSLog(@"%@--賣了一張票,還剩余%d張票",current,self.leftTicketsCount);
}else
{
//退出線程
[NSThread exit];
}
}
}
}
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
//開啟線程
[self.thread1 start];
[self.thread2 start];
[self.thread3 start];
}
@end
執(zhí)行效果圖
互斥鎖的優(yōu)缺點(diǎn)
優(yōu)點(diǎn):能有效防止因多線程搶奪資源造成的數(shù)據(jù)安全問題
缺點(diǎn):需要消耗大量的CPU資源
互斥鎖的使用前提:多條線程搶奪同一塊資源
相關(guān)專業(yè)術(shù)語:線程同步,多條線程按順序地執(zhí)行任務(wù)
互斥鎖,就是使用了線程同步技術(shù)
四:原子和非原子屬性
OC在定義屬性時(shí)有nonatomic和atomic兩種選擇
atomic:原子屬性,為setter方法加鎖(默認(rèn)就是atomic)
nonatomic:非原子屬性,不會(huì)為setter方法加鎖
atomic加鎖原理
@property (assign, atomic) int age;
- (void)setAge:(int)age
{
@synchronized(self) {
_age = age;
}
}
原子和非原子屬性的選擇
nonatomic和atomic對(duì)比
- atomic:線程安全,需要消耗大量的資源
- nonatomic:非線程安全,適合內(nèi)存小的移動(dòng)設(shè)備
iOS開發(fā)的建議
- 所有屬性都聲明為nonatomic
- 盡量避免多線程搶奪同一塊資源
- 盡量將加鎖、資源搶奪的業(yè)務(wù)邏輯交給服務(wù)器端處理,減小移動(dòng)客戶端的壓力
相關(guān)文章
詳解iOS開發(fā) - 用AFNetworking實(shí)現(xiàn)https單向驗(yàn)證,雙向驗(yàn)證
這篇文章主要介紹了詳解iOS開發(fā) - 用AFNetworking實(shí)現(xiàn)https單向驗(yàn)證,雙向驗(yàn)證,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下。2016-12-12iOS App開發(fā)中修改UILabel默認(rèn)字體的方法
UILabel是控制字體顯示的主要方式,這里我們就來看看通過NSAttributedText和NSMutableAttributedText這兩個(gè)類或者用runtime的方式來在iOS App開發(fā)中修改UILabel默認(rèn)字體的方法2016-07-07Objective-C中字符串NSString的常用操作方法總結(jié)
這篇文章主要介紹了Objective-C中字符串NSString的常用操作方法總結(jié),Objective-C中NSString和NSMutableString這兩個(gè)類下包含了操作字符串的大多數(shù)方法,需要的朋友可以參考下2016-04-04iOS實(shí)現(xiàn)文件切片儲(chǔ)存并且上傳(仿斷點(diǎn)續(xù)傳機(jī)制)
這篇文章主要給大家介紹了關(guān)于iOS實(shí)現(xiàn)文件切片儲(chǔ)存并上傳仿斷點(diǎn)續(xù)傳機(jī)制的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧。2017-12-12實(shí)例講解iOS應(yīng)用的設(shè)計(jì)模式開發(fā)中的Visitor訪問者模式
這篇文章主要介紹了iOS應(yīng)用的設(shè)計(jì)模式開發(fā)中的Visitor訪問者模式的實(shí)例,示例代碼為傳統(tǒng)的Objective-C,需要的朋友可以參考下2016-03-03在iOS開發(fā)的Quartz2D使用中實(shí)現(xiàn)圖片剪切和截屏功能
這篇文章主要介紹了在iOS開發(fā)的Quartz2D使用中實(shí)現(xiàn)圖片剪切和截屏功能的方法,代碼基于傳統(tǒng)的Objective-C,需要的朋友可以參考下2015-12-12iOS實(shí)現(xiàn)UIScrollView的無限輪播功能(原理)詳解
在現(xiàn)在的一些App中常常見到圖片輪播器,一般用于展示廣告、新聞等數(shù)據(jù),下面這篇文章主要給大家介紹了關(guān)于iOS實(shí)現(xiàn)UIScrollView的無限輪播功能(原理)的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),需要的朋友可以參考下2018-09-09