1. 程式人生 > >iOS開發多線程篇—線程安全

iOS開發多線程篇—線程安全

object -- nss property 處理 壓力 nat 安全問題 加鎖

一、多線程的安全隱患

資源共享

1塊資源可能會被多個線程共享,也就是多個線程可能會訪問同一塊資源

比如多個線程訪問同一個對象、同一個變量、同一個文件

當多個線程訪問同一塊資源時,很容易引發數據錯亂和數據安全問題

示例一:

技術分享圖片

示例二:

技術分享圖片

問題代碼:

技術分享圖片
 1 //
 2 //  YYViewController.m
 3 //  05-線程安全
 4 //
 5 //  Created by apple on 14-6-23.
 6 //  Copyright (c) 2014年 itcase. All rights reserved.
 7 //
 8 
 9 
10 #import "YYViewController.h"
11 
12 @interface YYViewController ()
13 //剩余票數
14 
15 @property(nonatomic,assign) int leftTicketsCount;
16 @property(nonatomic,strong)NSThread *thread1;
17 @property(nonatomic,strong)NSThread *thread2;
18 @property(nonatomic,strong)NSThread *thread3;
19 
20 
21 @end
22 
23 
24 @implementation YYViewController
25 
26 
27 - (void)viewDidLoad
28 {
29     [super viewDidLoad];
30 
31     //默認有20張票
32 
33     self.leftTicketsCount=10;
34 
35     //開啟多個線程,模擬售票員售票
36 
37     self.thread1=[[NSThread alloc]initWithTarget:self selector:@selector(sellTickets) object:nil];
38 
39     self.thread1.name=@"售票員A";
40 
41     self.thread2=[[NSThread alloc]initWithTarget:self selector:@selector(sellTickets) object:nil];
42 
43     self.thread2.name=@"售票員B";
44 
45     self.thread3=[[NSThread alloc]initWithTarget:self selector:@selector(sellTickets) object:nil];
46     self.thread3.name=@"售票員C";
47 }
48 
49  
50 -(void)sellTickets
51 {
52     while (1) {
53         //1.先檢查票數
54         int count=self.leftTicketsCount;
55         if (count>0) {
56             //暫停一段時間
57             [NSThread sleepForTimeInterval:0.002];
58 
59             //2.票數-1
60            self.leftTicketsCount= count-1;
61  
62             //獲取當前線程
63             NSThread *current=[NSThread currentThread];
64             NSLog(@"%@--賣了一張票,還剩余%d張票",current,self.leftTicketsCount);
65         }else
66         {
67             //退出線程
68             [NSThread exit];
69         }
70     }
71 }
72 
73 
74 -(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
75 { 
76     //開啟線程
77 
78    [self.thread1 start];
79     [self.thread2 start];
80     [self.thread3 start];
81 
82 }
83 
84 @end
技術分享圖片

打印結果:

技術分享圖片

二、安全隱患分析

技術分享圖片

技術分享圖片

三、如何解決

互斥鎖使用格式

@synchronized(鎖對象) { // 需要鎖定的代碼 }

註意:鎖定1份代碼只用1把鎖,用多把鎖是無效的

代碼示例:

技術分享圖片
 1 //
 2 //  YYViewController.m
 3 //  05-線程安全
 4 //
 5 //  Created by apple on 14-6-23.
 6 //  Copyright (c) 2014年 itcase. All rights reserved.
 7 //
 8 
 9 #import "YYViewController.h"
10 
11 @interface YYViewController ()
12 
13 //剩余票數
14 @property(nonatomic,assign) int leftTicketsCount;
15 @property(nonatomic,strong)NSThread *thread1;
16 @property(nonatomic,strong)NSThread *thread2;
17 @property(nonatomic,strong)NSThread *thread3;
18 @end
19 
20 @implementation YYViewController
21 
22 - (void)viewDidLoad
23 {
24     [super viewDidLoad];
25     //默認有20張票
26     self.leftTicketsCount=10;
27     //開啟多個線程,模擬售票員售票
28 
29     self.thread1=[[NSThread alloc]initWithTarget:self selector:@selector(sellTickets) object:nil];
30 
31     self.thread1.name=@"售票員A";
32 
33     self.thread2=[[NSThread alloc]initWithTarget:self selector:@selector(sellTickets) object:nil];
34 
35     self.thread2.name=@"售票員B";
36 
37     self.thread3=[[NSThread alloc]initWithTarget:self selector:@selector(sellTickets) object:nil];
38 
39     self.thread3.name=@"售票員C";
40 }
41 
42 
43 -(void)sellTickets
44 {
45     while (1) {
46         @synchronized(self){//只能加一把鎖
47         //1.先檢查票數
48 
49         int count=self.leftTicketsCount;
50         if (count>0) {
51             //暫停一段時間
52             [NSThread sleepForTimeInterval:0.002];
53             //2.票數-1
54 
55            self.leftTicketsCount= count-1;
56             //獲取當前線程
57             NSThread *current=[NSThread currentThread];
58             NSLog(@"%@--賣了一張票,還剩余%d張票",current,self.leftTicketsCount);
59 
60         }else
61         {
62             //退出線程
63             [NSThread exit];
64         }
65         }
66     }
67 }
68 
69 
70 -(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
71 {
72 
73     //開啟線程
74    [self.thread1 start];
75     [self.thread2 start];
76     [self.thread3 start];
77 }
78 
79 @end
技術分享圖片

執行效果圖

技術分享圖片

互斥鎖的優缺點

優點:能有效防止因多線程搶奪資源造成的數據安全問題

缺點:需要消耗大量的CPU資源

互斥鎖的使用前提:多條線程搶奪同一塊資源

相關專業術語:線程同步,多條線程按順序地執行任務

互斥鎖,就是使用了線程同步技術

四:原子和非原子屬性

OC在定義屬性時有nonatomic和atomic兩種選擇

atomic:原子屬性,為setter方法加鎖(默認就是atomic)

nonatomic:非原子屬性,不會為setter方法加鎖

atomic加鎖原理

技術分享圖片
1 @property (assign, atomic) int age;
2 
3 - (void)setAge:(int)age
4 { 
5 
6     @synchronized(self) { 
7        _age = age;
8     }
9 }
技術分享圖片

原子和非原子屬性的選擇

nonatomic和atomic對比

atomic:線程安全,需要消耗大量的資源

nonatomic:非線程安全,適合內存小的移動設備

iOS開發的建議

所有屬性都聲明為nonatomic

盡量避免多線程搶奪同一塊資源

盡量將加鎖、資源搶奪的業務邏輯交給服務器端處理,減小移動客戶端的壓力

iOS開發多線程篇—線程安全