热门标签 | HotTags
当前位置:  开发笔记 > 运维 > 正文

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

这篇文章主要介绍了浅析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;

线程间通信示例 – 图片下载

2015111892145343.png (600×346)

代码如下:

//
//  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

@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块资源可能会被多个线程共享,也就是多个线程可能会访问同一块资源
比如多个线程访问同一个对象、同一个变量、同一个文件
当多个线程访问同一块资源时,很容易引发数据错乱和数据安全问题
示例一:

2015111892251282.png (664×387)

示例二:

2015111892312267.png (659×386)

问题代码:

代码如下:

//
//  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


打印结果:

2015111892340617.png (871×448)

二、安全隐患分析

2015111892405061.png (625×320)

2015111892430149.png (678×379)

三、如何解决
 
互斥锁使用格式
@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


执行效果图

2015111892457124.png (876×191)

互斥锁的优缺点
优点:能有效防止因多线程抢夺资源造成的数据安全问题
缺点:需要消耗大量的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
  • 尽量避免多线程抢夺同一块资源
  • 尽量将加锁、资源抢夺的业务逻辑交给服务器端处理,减小移动客户端的压力

推荐阅读
  • Docker的安全基准
    nsitionalENhttp:www.w3.orgTRxhtml1DTDxhtml1-transitional.dtd ... [详细]
  • 优化联通光猫DNS服务器设置
    本文详细介绍了如何为联通光猫配置DNS服务器地址,以提高网络解析效率和访问体验。通过智能线路解析功能,域名解析可以根据访问者的IP来源和类型进行差异化处理,从而实现更优的网络性能。 ... [详细]
  • 在计算机技术的学习道路上,51CTO学院以其专业性和专注度给我留下了深刻印象。从2012年接触计算机到2014年开始系统学习网络技术和安全领域,51CTO学院始终是我信赖的学习平台。 ... [详细]
  • 1:有如下一段程序:packagea.b.c;publicclassTest{privatestaticinti0;publicintgetNext(){return ... [详细]
  • 深入理解 SQL 视图、存储过程与事务
    本文详细介绍了SQL中的视图、存储过程和事务的概念及应用。视图为用户提供了一种灵活的数据查询方式,存储过程则封装了复杂的SQL逻辑,而事务确保了数据库操作的完整性和一致性。 ... [详细]
  • 当iOS设备越狱后,某些插件可能会导致系统崩溃(白苹果)。此时,可以通过进入安全模式来排查并删除有问题的插件。本文将详细介绍如何通过特定按键组合进入不加载MobileSubstrate的安全模式,并提供相关背景知识。 ... [详细]
  • 本文详细介绍了Java编程语言中的核心概念和常见面试问题,包括集合类、数据结构、线程处理、Java虚拟机(JVM)、HTTP协议以及Git操作等方面的内容。通过深入分析每个主题,帮助读者更好地理解Java的关键特性和最佳实践。 ... [详细]
  • 如何配置Unturned服务器及其消息设置
    本文详细介绍了Unturned服务器的配置方法和消息设置技巧,帮助用户了解并优化服务器管理。同时,提供了关于云服务资源操作记录、远程登录设置以及文件传输的相关补充信息。 ... [详细]
  • 网络攻防实战:从HTTP到HTTPS的演变
    本文通过一系列日记记录了从发现漏洞到逐步加强安全措施的过程,探讨了如何应对网络攻击并最终实现全面的安全防护。 ... [详细]
  • 360SRC安全应急响应:从漏洞提交到修复的全过程
    本文详细介绍了360SRC平台处理一起关键安全事件的过程,涵盖从漏洞提交、验证、排查到最终修复的各个环节。通过这一案例,展示了360在安全应急响应方面的专业能力和严谨态度。 ... [详细]
  • CentOS 7 磁盘与文件系统管理指南
    本文详细介绍了磁盘的基本结构、接口类型、分区管理以及文件系统格式化等内容,并提供了实际操作步骤,帮助读者更好地理解和掌握 CentOS 7 中的磁盘与文件系统管理。 ... [详细]
  • Windows服务与数据库交互问题解析
    本文探讨了在Windows 10(64位)环境下开发的Windows服务,旨在定期向本地MS SQL Server (v.11)插入记录。尽管服务已成功安装并运行,但记录并未正确插入。我们将详细分析可能的原因及解决方案。 ... [详细]
  • 探讨如何通过编程技术实现100个并发连接,解决线程创建顺序问题,并提供高效的并发测试方案。 ... [详细]
  • 本周信息安全小组主要进行了CTF竞赛相关技能的学习,包括HTML和CSS的基础知识、逆向工程的初步探索以及整数溢出漏洞的学习。此外,还掌握了Linux命令行操作及互联网工作原理的基本概念。 ... [详细]
  • 本文详细介绍了如何使用PHP检测AJAX请求,通过分析预定义服务器变量来判断请求是否来自XMLHttpRequest。此方法简单实用,适用于各种Web开发场景。 ... [详细]
author-avatar
WSSDRED_935
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有