以前对多线程总是有点陌生感,虽然平时也一样在用,但总是感觉不够亲密。这两天就彻底整理一下,但是由于是demo代码那种,所以在这里我就不再重复敲字了,直接上代码吧。
//
// JSC_NSThread.m
// ios多线程学习
//
// Created by huasu on 17/2/28.
// Copyright © 2017年 JY. All rights reserved.
//
//NSThread实现的技术有下面三种:cocoa thread , POSIX threads ,Multiprocessing Services 一般使用的是cocoa thread 技术
/*
NSThread 每个NSThread对象对应一个线程,量级较轻(真正的多线程)
–优点:NSThread 比其他两个轻量级,使用简单
–缺点:需要自己管理线程的生命周期、线程同步、加锁、睡眠以及唤醒等。线程同步对数据的加锁会有一定的系统开销
*/
#import "JSC_NSThread.h"
@interface JSC_NSThread ()
#define kURL @"http://s10.sinaimg.cn/mw690/56279263h7bdc5a0791f9&690.png"
@property (nonatomic,weak)UIImageView *imageview;
@property (nonatomic,strong)NSThread *thread;
@end
@implementation JSC_NSThread
-(void)learn
{
//NSThread有两种直接创建方式:
//1> 类方法直接开启后台线程,并执行选择器方法 detachNewThreadSelector
// 新建一个线程,调用@selector方法
[NSThread detachNewThreadSelector:@selector(bigDemo:) toTarget:self withObject:nil];
//2> 成员方法,在实例化线程对象之后,需要使用start执行选择器方法 initWithTarget
// 成员方法
NSThread *thread = [[NSThread alloc] initWithTarget:self selector:@selector(bigDemo:) object:nil];
// 启动start线程
[thread start];
// 参数:
// selector :线程执行的方法,这个selector只能有一个参数,而且不能有返回值。
// target :selector消息发送的对象
// argument:传输给target的唯一参数,也可以是nil,即object后面的参数
// 不显式创建线程的方法
// 用NSObject的类方法 performSelectorInBackground:withObject: 创建一个线程:
//对于NSThread的简单使用,可以用NSObject的performSelectorInBackground替代
// performSelectorInBackground是将bigDemo的任务放在后台线程中执行
[self performSelectorInBackground:@selector(bigDemo) withObject:nil];
}
- (void)viewDidLoad {
[super viewDidLoad];
UIImageView *imageview=[[UIImageView alloc]init];
self.imageview=imageview;
imageview.frame=CGRectMake(10, 100, 200, 200);
[self.view addSubview:imageview];
UIButton *button=[UIButton buttonWithType:UIButtonTypeRoundedRect];
button.frame=CGRectMake(50, 400, 220, 25);
[button setTitle:@"加载图片" forState:UIControlStateNormal];
//添加方法
[button addTarget:self action:@selector(loadImageWithMultiThread) forControlEvents:UIControlEventTouchUpInside];
[self.view addSubview:button];
//如果网络不好,我们需要停止线程
UIButton *btn=[UIButton buttonWithType:UIButtonTypeRoundedRect];
btn.frame=CGRectMake(50, 500, 220, 25);
[btn setTitle:@"停止加载" forState:UIControlStateNormal];
//停止线程
[btn addTarget:self action:@selector(stopLoadImage) forControlEvents:UIControlEventTouchUpInside];
[self.view addSubview:btn];
}
-(void)stopLoadImage
{
//判断线程是否完成,如果没有完成则设置为取消状态
//注意设置为取消状态仅仅是改变了线程状态而言,并不能终止线程
if (!self.thread.isFinished) {
[self.thread cancel];
}
}
-(void)loadImageWithMultiThread
{
//这样我们点击button时会创建一个子线程去执行任务,这样加载图片的时候我们主线程的ui就不会卡死
//如果多个线程加载图片,线程优先级范围为0~1,值越大优先级越高,每个线程的优先级默认为0.5 优先级越大的会越先执行,但未必第一个加载,首先代码中线程的启动顺序不一定,还有就是网络问题我们没法控制,每个线程执行时实际网络状况很可能不一致
NSThread *thread = [[NSThread alloc]initWithTarget:self selector:@selector(downloadImage:) object:kURL];
self.thread=thread;
[thread start];
}
-(void)updateUI:(UIImage*) image{
self.imageview.image = image;
}
-(void)downloadImage:(NSString *) url{
NSData *data = [[NSData alloc] initWithContentsOfURL:[NSURL URLWithString:url]];
UIImage *image = [[UIImage alloc]initWithData:data];
if(image == nil){
}else{
[self performSelectorOnMainThread:@selector(updateUI:) withObject:image waitUntilDone:YES];
}
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
@end
说到多线程了,肯定少不了的就是线程锁了,下面附一个经典的卖票例子吧
//
// JSC_ticket.m
// ios多线程学习
//
// Created by huasu on 17/3/1.
// Copyright © 2017年 JY. All rights reserved.
//
#import "JSC_ticket.h"
@interface JSC_ticket ()
{
int sum;//原始总票数
int tickets;//当前票数
int count;//售出票数
NSThread* ticketsThreadone;
NSThread* ticketsThreadtwo;
NSCondition* ticketsCondition;//线程锁
NSLock *theLock;//线程锁
}
@end
@implementation JSC_ticket
- (void)viewDidLoad {
[super viewDidLoad];
//线程同步
[self text1];
// //线程的顺序执行 他们都可以通过[ticketsCondition signal]; 发送信号的方式,在一个线程唤醒另外一个线程的等待。
// [self text2];
// 其他同步
// 我们可以使用指令 @synchronized 来简化 NSLock的使用,这样我们就不必显示编写创建NSLock,加锁并解锁相关代码。
// - (void)doSomeThing:(id)anObj
// {
// @synchronized(anObj)
// {
// // Everything between the braces is protected by the @synchronized directive.
// }
// }
// 还有其他的一些锁对象,比如:循环锁NSRecursiveLock,条件锁NSConditionLock,分布式锁NSDistributedLock等等,可以自己看官方文档学习
}
-(void)text1
{
sum =100;
tickets = 100;
count = 0;
theLock = [[NSLock alloc] init];
// 锁对象
ticketsCondition = [[NSCondition alloc] init];
ticketsThreadone = [[NSThread alloc] initWithTarget:self selector:@selector(run1) object:nil];
[ticketsThreadone setName:@"Thread-1"];
[ticketsThreadone start];
ticketsThreadtwo = [[NSThread alloc] initWithTarget:self selector:@selector(run1) object:nil];
[ticketsThreadtwo setName:@"Thread-2"];
[ticketsThreadtwo start];
}
-(void)text2
{
sum =100;
tickets = 100;
count = 0;
theLock = [[NSLock alloc] init];
// 锁对象
ticketsCondition = [[NSCondition alloc] init];
ticketsThreadone = [[NSThread alloc] initWithTarget:self selector:@selector(run2) object:nil];
[ticketsThreadone setName:@"Thread-1"];
[ticketsThreadone start];
ticketsThreadtwo = [[NSThread alloc] initWithTarget:self selector:@selector(run2) object:nil];
[ticketsThreadtwo setName:@"Thread-2"];
[ticketsThreadtwo start];
NSThread *ticketsThreadthree = [[NSThread alloc] initWithTarget:self selector:@selector(run3) object:nil];
[ticketsThreadthree setName:@"Thread-3"];
[ticketsThreadthree start];
}
- (void)run1{
while (TRUE) {
// 上锁
// [ticketsCondition lock];
[theLock lock];
if(tickets >= 0){
[NSThread sleepForTimeInterval:0.09];
count = sum - tickets;
NSLog(@"当前票数是:%d,售出:%d,线程名:%@",tickets,count,[[NSThread currentThread] name]);
tickets--;
}else{
break;//退出while死循环
}
[theLock unlock];
// [ticketsCondition unlock];
}
//如果没有线程同步的lock,卖票数可能是-1.加上lock之后线程同步保证了数据的正确性。
//上面例子我使用了两种锁,一种NSCondition ,一种是:NSLock。 NSCondition我已经注释了。
}
- (void)run2{
while (TRUE) {
// 上锁
[ticketsCondition lock];
[ticketsCondition wait];
[theLock lock];
if(tickets >= 0){
[NSThread sleepForTimeInterval:0.09];
count = 100 - tickets;
NSLog(@"当前票数是:%d,售出:%d,线程名:%@",tickets,count,[[NSThread currentThread] name]);
tickets--;
}else{
break;
}
[theLock unlock];
[ticketsCondition unlock];
}
}
-(void)run3{
while (YES) {
[ticketsCondition lock];
[NSThread sleepForTimeInterval:3];
[ticketsCondition signal];
[ticketsCondition unlock];
}
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
/*
#pragma mark - Navigation
// In a storyboard-based application, you will often want to do a little preparation before navigation
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
// Get the new view controller using [segue destinationViewController].
// Pass the selected object to the new view controller.
}
*/
@end