浅析iOS应用开发中线程间的通信与线程安全问题

线程间的通信
 
简单说明
线程间通信:在1个进程中,线程往往不是孤立存在的,多个线程之间需要经常进行通信
 
线程间通信的体现
1个线程传递数据给另1个线程
在1个线程中执行完特定任务后,转到另1个线程继续执行任务
 
线程间通信常用方法

代码如下:

- (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
{

// 在子线程中调用download方法下载图片
    [self performSelectorInBackground:@selector(download) withObject:nil];
}

-(void)download
{
    //1.根据URL下载图片
    //从网络中下载图片
    NSURL *urlstr=[NSURL URLWithString:@"fdsf"];

//把图片转换为二进制的数据
    NSData *data=[NSData dataWithContentsOfURL:urlstr];//这一行操作会比较耗时

//把数据转换成图片
    UIImage *image=[UIImage imageWithData:data];
 
    //2.回到主线程中设置图片
    [self performSelectorOnMainThread:@selector(settingImage:) withObject:image waitUntilDone:NO];
}

//设置显示图片
-(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
{
// 在子线程中调用download方法下载图片

[self performSelectorInBackground:@selector(download) withObject:nil];
}

-(void)download
{

//1.根据URL下载图片
    //从网络中下载图片
    NSURL *urlstr=[NSURL URLWithString:@"fdsf"];

//把图片转换为二进制的数据
    NSData *data=[NSData dataWithContentsOfURL:urlstr];//这一行操作会比较耗时

//把数据转换成图片
    UIImage *image=[UIImage imageWithData:data];

//2.回到主线程中设置图片
    //第一种方式
//    [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];
}

//设置显示图片
//-(void)settingImage:(UIImage *)image
//{
//    self.iconView.image=image;
//}

@end

线程安全
 
一、多线程的安全隐患
资源共享
1块资源可能会被多个线程共享,也就是多个线程可能会访问同一块资源
比如多个线程访问同一个对象、同一个变量、同一个文件
当多个线程访问同一块资源时,很容易引发数据错乱和数据安全问题
示例一:

示例二:

问题代码:

代码如下:

//
//  YYViewController.m
//  05-线程安全
//
//  Created by apple on 14-6-23.
//  Copyright (c) 2014年 itcase. All rights reserved.
//

#import "YYViewController.h"

@interface YYViewController ()
//剩余票数

@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];

//默认有20张票

self.leftTicketsCount=10;

//开启多个线程,模拟售票员售票

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.先检查票数
        int count=self.leftTicketsCount;
        if (count>0) {
            //暂停一段时间
            [NSThread sleepForTimeInterval:0.002];

//2.票数-1
           self.leftTicketsCount= count-1;
 
            //获取当前线程
            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

打印结果:

二、安全隐患分析

三、如何解决
 
互斥锁使用格式
@synchronized(锁对象) { // 需要锁定的代码  }
注意:锁定1份代码只用1把锁,用多把锁是无效的
 
代码示例:

代码如下:

//
//  YYViewController.m
//  05-线程安全
//
//  Created by apple on 14-6-23.
//  Copyright (c) 2014年 itcase. All rights reserved.
//

#import "YYViewController.h"

@interface YYViewController ()

//剩余票数
@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];
    //默认有20张票
    self.leftTicketsCount=10;
    //开启多个线程,模拟售票员售票

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.先检查票数

int count=self.leftTicketsCount;
        if (count>0) {
            //暂停一段时间
            [NSThread sleepForTimeInterval:0.002];
            //2.票数-1

self.leftTicketsCount= count-1;
            //获取当前线程
            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

执行效果图

互斥锁的优缺点
优点:能有效防止因多线程抢夺资源造成的数据安全问题
缺点:需要消耗大量的CPU资源
 
互斥锁的使用前提:多条线程抢夺同一块资源
相关专业术语:线程同步,多条线程按顺序地执行任务
互斥锁,就是使用了线程同步技术
 
四:原子和非原子属性
 
OC在定义属性时有nonatomic和atomic两种选择
atomic:原子属性,为setter方法加锁(默认就是atomic)
nonatomic:非原子属性,不会为setter方法加锁
 
atomic加锁原理

代码如下:

@property (assign, atomic) int age;

- (void)setAge:(int)age
{

@synchronized(self) {
       _age = age;
    }
}

原子和非原子属性的选择
nonatomic和atomic对比

  • atomic:线程安全,需要消耗大量的资源
  • nonatomic:非线程安全,适合内存小的移动设备

iOS开发的建议

  • 所有属性都声明为nonatomic
  • 尽量避免多线程抢夺同一块资源
  • 尽量将加锁、资源抢夺的业务逻辑交给服务器端处理,减小移动客户端的压力
(0)

相关推荐

  • iOS实现动态的开屏广告示例代码

    一.实现效果图 二.实现思路: 用一个固定的png图片左启动图,应该和广告视图需要进行动画的期初的位置一致,当启动图消失的时候,呈现出图片,实际遇到的困难是,因为广告图片是从网络请求加载的,当时把广告视图放在了请求数据的块里面,广告出现的时候会闪一下,放在外面就没事了. 三.实现示例 1.广告的头文件 // XBAdvertView.h // scoreCount // // Created by 王国栋 on 15/12/22. // Copyright © 2015年 xiaobai. Al

  • IOS设置按钮为圆角的示例代码

    iOS中很多时候都需要用到指定风格的圆角按钮,以下是UIButton提供的创建圆角按钮方法 设置按钮的4个角: 左上:UIRectCornerTopLeft 左下:UIRectCornerBottomLeft 右上:UIRectCornerTopRight 右下:UIRectCornerBottomRight 示例代码: UIButton *button = [[UIButton alloc] initWithFrame:CGRectMake(50, 60, 80, 40)]; button.b

  • IOS轻松几步实现自定义转场动画

    一.系统提供的转场动画 目前,系统给我们提供了push/pops和present/dismiss两种控制器之间跳转方.当然,通过设置UIModalTransitionStyle属性,可以实现下面4种modal效果,相信大家都比较熟悉了,这里就不再展示效果图. UIModalTransitionStyleCoverVertical // 从下往上, UIModalTransitionStyleFlipHorizontal // 水平翻转 UIModalTransitionStyleCrossDis

  • php、java、android、ios通用的3des方法(推荐)

    php服务器,java服务器,android,ios开发兼容的3des加密解密, php <?php class DES3 { var $key = "my.oschina.net/penngo?#@"; var $iv = "01234567"; function encrypt($input){ $size = mcrypt_get_block_size(MCRYPT_3DES,MCRYPT_MODE_CBC); $input = $this->pk

  • ionic在开发ios系统微信时键盘挡住输入框的解决方法(键盘弹出问题)

    在使用ionic开发IOS系统微信的时候会有一个苦恼的问题,填写表单的时候键盘会挡住输入框,其实并不算什么大问题,只要用户输入一个字就可以立刻看见输入框了. 可惜的是,有些客户是不讲理的,他才不管这个问题,反正就是不行,所以在一天睡觉的时候突然惊醒,想出来这个方案. 我就不仔细讲代码了,直接上图 angular.module('MyApp') .directive('focusInput', ['$ionicScrollDelegate', '$window', '$timeout', '$io

  • Android开发实现带有反弹效果仿IOS反弹scrollview教程详解

    首先给大家看一下我们今天这个最终实现的效果图: 这个是ios中的反弹效果.当然我们安卓中如果想要实现这种效果,感觉不会那么生硬,滚动到底部或者顶部的时候.当然 使用scrollview是无法实现的.所以我们需要新建一个view继承ScrollView package davidbouncescrollview.qq986945193.com.davidbouncescrollview; import android.annotation.SuppressLint; import android.

  • iOS App通信之local socket示例

    之前看到一篇文章介绍到App之间的五种通信方式,它分别有URL Scheme,Keychain,UIPastedboard,UIDocumentInteractionController以及利用socket进行本地通信.前面4种都有用到过,也相对比较简单,几行代码的事.对于最后一种之前一直没用到过(原谅我还是个小白),所以今天试着写了下,这儿记录在这里和大家分享. 好了,废话不多说,开始: 首先,说下它的原理,其实很简单,一个App在本地的端口进行TCP的bind和listen,另外一个App在

  • IOS绘制虚线的方法总结

    一.重写drawRect方法. - (void)drawRect:(CGRect)rect { [super drawRect:rect]; CGContextRef currentContext = UIGraphicsGetCurrentContext(); //设置虚线颜色 CGContextSetStrokeColorWithColor(currentContext, [UIColor BlackColor].CGColor); //设置虚线宽度 CGContextSetLineWidt

  • iOS动画教你编写Slack的Loading动画进阶篇

    前几天看了一篇关于动画的博客叫手摸手教你写 Slack 的 Loading 动画,看着挺炫,但是是安卓版的,寻思的着仿造着写一篇iOS版的,下面是我写这个动画的分解~ 老规矩先上图和demo地址: 刚看到这个动画的时候,脑海里出现了两个方案,一种是通过drawRect画出来,然后配合CADisplayLink不停的绘制线的样式:第二种是通过CAShapeLayer配合CAAnimation来实现动画效果.再三考虑觉得使用后者,因为前者需要计算很多,比较复杂,而且经过测试前者相比于后者消耗更多的C

  • iOS应用中使用AsyncSocket库处理Socket通信的用法讲解

    用socket可以实现像QQ那样发送即时消息的功能.客户端和服务端需要建立长连接,在长连接的情况下,发送消息.客户端可以发送心跳包来检测长连接. 在iOS开发中使用socket,一般都是用第三方库AsyncSocket,不得不承认这个库确实很强大.下载地址CocoaAsyncSocket . 特性 AsyncSocket类是支持TCP的. AsyncUdpSocket是支持UDP的. AsyncSocket是封装了CFSocket和CFSteam的TCP/IP socket网络库.它提供了异步操

随机推荐