iOS开发学习之多线程


  • 多线程
1进程指在系统运行的程序  如:QQ Xcode两个正在运行的进程
2每一个进程至少有一条线程,进程的所有的任务都在线程中执行,线程是进程的基本执行单元。
3线程的串行,一条线程只能执行一个任务,可以认为线程是进程中的执行路径
4原理:进程好比车间,线程好比车间工人。同一时间内,CPU只处理一条线程,多线程并发执行,其实是CPU快速地在多线程之间调度和切换。
5过度使用开启多个线程会造成不好的后果 1.CPU会累死 2.每条线程被调度执行的频率会降低 3.占用一定的内存空间 4.CPU调度线程的开销就越大
6实现方案:Pthread/NSThread/GCD/NSOperation
  • 主线程
作用:1.显示UI界面  2.处理UI事件(点击,滚动,拖拉)
注意:不要将耗时的操作放到主线程中,不然会出现“卡”的不良用户体验。解决措施:将耗时操作放在子线程(非主线程/后台线程),这样用户点击就能做出反应,能同时处理耗时操作和UI控件事件。  
  • 多线程的多种创建方式
1.Pthread创建子线程 不过不常用。

/**
 *  1.pthread创建子线程
 */
-(void)pthread
{
    NSThread *currentThread = [NSThread currentThread];
    NSLog(@"%@", currentThread);

    pthread_t threadId;
    pthread_create(&threadId, NULL, run, NULL);
}
void *run(void *data)
{
    NSThread *currentTread = [NSThread currentThread];
    NSLog(@" %@",currentTread);
    return NULL;
}


2.NSThread可通过initWithTarget:Selector:Object创建一条子线程,并用start方法开启线程。


/**
 *   2.用NSThread创建子线程并start开启线程
 */
-(void)NSThread
{
    // 创建子线程
    NSThread *currentThread1 = [[NSThread alloc] initWithTarget:self selector:@selector(run:) object:@"JHA"];
    currentThread1.name = @"线程A";
    
    // 开启线程
    [currentThread1 start];
    
    // 创建子线程
    NSThread *currentThread2 = [[NSThread alloc] initWithTarget:self selector:@selector(run:) object:@"JHB"];
    currentThread2.name = @"线程B";
    
    // 开启线程
    [currentThread2 start];
    
}


3.隐式创建子线程并启动

/**
 *  隐式创建子线程自动启动线程 (在后台进行)
 */
-(void)performSelectorInBackground
{
    // 在后台执行 == 在子线程执行
    [self performSelectorInBackground:@selector(run:) withObject:@"performSlectorInback"];
}



4.创建线程后自动启动线程

/**
 *  4.断开分成两条新的线程
 */
-(void)detachNewThread
{
    [NSThread detachNewThreadSelector:@selector(run:) toTarget:self withObject:@"detachNewThread.A"];
    [NSThread detachNewThreadSelector:@selector(run:) toTarget:self withObject:@"detachNewThread.B"];
}

-(void)run:(NSString *)param
{
    for (int i=0; i<100; i++) {
        NSThread *current = [NSThread currentThread];
        NSLog(@" %@ --run -- %@",current,param);
    }
}

第3种方式和第4种方式的缺点:无法对线程进行更详细的设置

  • 多线程的安全隐患
解决措施:加锁
枷锁格式:@synchronized(锁对象){}  注意:只能是同一把锁
加锁前提:多条线程抢夺同一块资源
线程同步:表示多条线程按顺序地执行,其实本质就是在线程当中加锁
我们在开发项目的时候,一些特殊属性 它们也被加了锁。
atomic:原子属性.会为setter方法加锁 
nonatomic:非原子属性,不会为setter方法加锁
atomic:线程安全 需要消耗大量的资源
nonatomic:非线程安全,适用内存小的移动设备
先下代码说明多线程抢夺资源后引发的安全性问题。

//
//  JHViewController.m
//  线程安全
//
//  Created by cjj on 15-10-28.
//  Copyright (c) 2015年 jh.chen. All rights reserved.
//

#import "JHViewController.h"

@interface JHViewController ()
@property (nonatomic, strong) NSThread *thread1;
@property (nonatomic, strong) NSThread *thread2;
@property (nonatomic, strong) NSThread *thread3;
@property (nonatomic, assign) int leftTicketsCount;
@property (nonatomic, strong) NSLock *lock;

@end

@implementation JHViewController

- (void)viewDidLoad
{
    [super viewDidLoad];
    self.leftTicketsCount = 100;
    
    self.thread1 = [[NSThread alloc] initWithTarget:self selector:@selector(sale) object:nil];
    self.thread1.name = @"售票员A";
    
    self.thread2 = [[NSThread alloc] initWithTarget:self selector:@selector(sale) object:nil];
    self.thread2.name = @"售票员B";
    
    self.thread3 = [[NSThread alloc] initWithTarget:self selector:@selector(sale) object:nil];
    self.thread3.name = @"售票员C";
    self.lock = [[NSLock alloc] init];
}

-(void)sale
{
    while (1) {
//        @synchronized (self.lock){ // 加锁
            int count = self.leftTicketsCount;
            
            if (count > 0) {
                [NSThread sleepForTimeInterval:0.02];
                self.leftTicketsCount = count - 1;
                NSThread *current = [NSThread currentThread];
                NSLog(@"%@ 卖了一张 还剩下 %d张", current.name,self.leftTicketsCount);
                
            } else {
                [NSThread exit];
            }
//        } // 解锁
    }
}

-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
    [self.thread1 start];
    [self.thread2 start];
    [self.thread3 start];
}
@end


通过运行打印 我们知道有重复的数值。这就是抢夺同一资源的问题。至于解决方法就是往其加锁。打开上面代码注释边即可。
  • 线程间通信
定义:线程往往不是孤立存在,多个线程之间需要经常进行通信。
例如:下面子线程和主线程之间的切换。

#import "JHViewController.h"


@interface JHViewController ()
@property (weak, nonatomic) IBOutlet UIImageView *imageView;

@end

@implementation JHViewController

- (void)viewDidLoad
{
    [super viewDidLoad];
}

-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
    // 下载图片 (开启子线程)
    [self performSelectorInBackground:@selector(download) withObject:nil];
}

/**
 *  下载图片 : 子线程
 */
-(void)download
{
    // 根据URL下载图片
    NSURL *url = [NSURL URLWithString:@"www.baidu.com"];
    NSData *data = [NSData dataWithContentsOfURL:url];
    UIImage *image = [UIImage imageWithData:data];
    
    // 回到主线程
    [self performSelectorOnMainThread:@selector(settingImage:) withObject:image waitUntilDone:NO];
//    [self.imageView performSelectorOnMainThread:@selector(settingImage:) withObject:image waitUntilDone:NO];
}

/**
 * 显示图片: 主线程
 */
-(void)settingImage:(UIImage *)image;
{
    self.imageView.image = image;
}
@end




  • GCD “牛逼的中枢调度器” 
优势:
苹果公司为多核的并行运算提出的解决方法
GCD会自动利用更多的CPU内核
GCD会自动管理线程的生命周期
程序员只告诉GCD想要执行什么任务,无需写线程管理代码
GCD的使用步骤:1.定制任务 2.将任务添加到队列中(fifo 先进先出)

GCD中有2个用来执行任务的函数:
1、用同步的方式执行任务 :dispatch_sync;
2、用异步的方式执行任务:dispatch_async;
延时执行:
1、调用NSObject的方法[self performSelector:withObject:afterDelay:];
2、使用GCD函数  dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(<#delayInSeconds#> * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
    });
一次性代码:    
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
 });
常见术语解释 同步.异步.并发.串行:
^ 同步:在当前线程中执行任务,不具备开启新线程能力
^异步:在新的线程中执行任务,具备开启新线程能力
^并发:多个任务同时执行
^串行:一个任务执行完后,再执行下一个任务
同步函数和异步函数:
同步函数
1.并发队列:不会开线程
2.串行队列:不会开线程
异步函数:
1.并发队列:能开N条线程
2.串行队列:开启一条线程

如果有什么建议 请联系虾米:
QQ:584837022
微信:foreverlovewillgoon

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值