学习了下关于iOS多线程的基本操作,记录下笔记。
学习阶段,如果哪部分有问题,欢迎指正,共同探讨。@Apach3
新建Xcode工程,选择Single View Application,语言为Objective-C。文件夹目录如下:
下面是相关文件代码:
ViewController.h:
//
// ViewController.h
// TestThread
//
// Created by APACHE on 2017/10/21.
// Copyright © 2017年 APACHE. All rights reserved.
//
#import <UIKit/UIKit.h>
@interface ViewController : UIViewController
@property (nonatomic, strong) NSOperationQueue *operationQueue;
@end
ViewController.m:
//
// ViewController.m
// TestThread
//
// Created by APACHE on 2017/10/21.
// Copyright © 2017年 APACHE. All rights reserved.
//
/*
总结:
1.进程:并发执行的程序在执行过程中分配和管理内存资源的基本单位,比如启动一个app就是一个进程。
线程:cpu调度的基本单位,线程必须被包含在进程中。一个进程可以有多个线程。
2.多线程实现原理:单核cpu下,多个线程同时执行的时候是通过分配时间片的方式来交替的执行线程。
3.串行:多个任务按照一定顺序去执行。
并行:多个任务并发执行。
4.多线程优缺点:
优点:(1)简化了编程模型
(2)更加的轻量级
(3)提高了执行效率
(4)提高了资源利用率
缺点:(1)增加程序设计复杂性
(2)占用内存空间
(3)增大cpu调度开销
5.iOS实现多线程的方式:pThread、NSThread、GCD、NSOperation
*/
#import "ViewController.h"
#import <pthread.h>
#import "TicketManager.h"
#import "TestSingle.h"
#import "CustomOperation.h"
@interface ViewController ()
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
// TicketManager *manager = [[TicketManager alloc] init];
// [manager startToSale];
UIButton *pThreadBtn = [UIButton buttonWithType:UIButtonTypeCustom];
pThreadBtn.frame = CGRectMake(100, 100, 300, 100);
[pThreadBtn setBackgroundColor:[UIColor redColor]];
[pThreadBtn setTitle:@"pThreadBtn" forState:UIControlStateNormal];
[pThreadBtn setTitleColor:[UIColor blackColor] forState:UIControlStateNormal];
[pThreadBtn addTarget:self action:@selector(clickPThread) forControlEvents:UIControlEventTouchUpInside];
[self.view addSubview:pThreadBtn];
UIButton *nsThreadBtn = [UIButton buttonWithType:UIButtonTypeCustom];
nsThreadBtn.frame = CGRectMake(100, 200, 300, 100);
[nsThreadBtn setBackgroundColor:[UIColor redColor]];
[nsThreadBtn setTitle:@"nsThreadBtn" forState:UIControlStateNormal];
[nsThreadBtn setTitleColor:[UIColor blackColor] forState:UIControlStateNormal];
[nsThreadBtn addTarget:self action:@selector(clickNSThreadBtn) forControlEvents:UIControlEventTouchUpInside];
[self.view addSubview:nsThreadBtn];
UIButton *gcdThreadBtn = [UIButton buttonWithType:UIButtonTypeCustom];
gcdThreadBtn.frame = CGRectMake(100, 300, 300, 100);
[gcdThreadBtn setBackgroundColor:[UIColor redColor]];
[gcdThreadBtn setTitle:@"GCDBtn" forState:UIControlStateNormal];
[gcdThreadBtn setTitleColor:[UIColor blackColor] forState:UIControlStateNormal];
[gcdThreadBtn addTarget:self action:@selector(clickGCDBtn) forControlEvents:UIControlEventTouchUpInside];
[self.view addSubview:gcdThreadBtn];
UIButton *singleBtn = [UIButton buttonWithType:UIButtonTypeCustom];
singleBtn.frame = CGRectMake(100, 400, 300, 100);
[singleBtn setBackgroundColor:[UIColor redColor]];
[singleBtn setTitle:@"singleBtn" forState:UIControlStateNormal];
[singleBtn setTitleColor:[UIColor blackColor] forState:UIControlStateNormal];
[singleBtn addTarget:self action:@selector(clickSingleBtn) forControlEvents:UIControlEventTouchUpInside];
[self.view addSubview:singleBtn];
UIButton *afterBtn = [UIButton buttonWithType:UIButtonTypeCustom];
afterBtn.frame = CGRectMake(100, 500, 300, 100);
[afterBtn setBackgroundColor:[UIColor redColor]];
[afterBtn setTitle:@"afterBtn" forState:UIControlStateNormal];
[afterBtn setTitleColor:[UIColor blackColor] forState:UIControlStateNormal];
[afterBtn addTarget:self action:@selector(clickAfterBtn) forControlEvents:UIControlEventTouchUpInside];
[self.view addSubview:afterBtn];
UIButton *nsOperationBtn = [UIButton buttonWithType:UIButtonTypeCustom];
nsOperationBtn.frame = CGRectMake(100, 600, 300, 100);
[nsOperationBtn setBackgroundColor:[UIColor redColor]];
[nsOperationBtn setTitle:@"nsOperationBtn" forState:UIControlStateNormal];
[nsOperationBtn setTitleColor:[UIColor blackColor] forState:UIControlStateNormal];
[nsOperationBtn addTarget:self action:@selector(clickNSOperationBtn) forControlEvents:UIControlEventTouchUpInside];
[self.view addSubview:nsOperationBtn];
}
#pragma mark - pThread Test
- (void)clickPThread {
NSLog(@"我在pthread主线程");
pthread_t pthread; //定义pthread
pthread_create(&pthread, NULL, run, NULL); //创建线程
}
void *run (void *data) {
NSLog(@"我在pThread子线程");
for (int i=0; i<10; i++) {
NSLog(@"%d", i);
sleep(1);
}
return NULL;
}
#pragma mark - NSThread Test
- (void)clickNSThreadBtn {
NSLog(@"我在NSThread主线程");
// 1.alloc init 创建线程(有自己的对象,需要start启动;2、3种没有自己的对象,所以无法使用相关的属性方法)
NSThread *thread1 = [[NSThread alloc] initWithTarget:self selector:@selector(runNSThread) object:nil];
[thread1 setName:@"Name_thread1"]; //属性1:name
[thread1 setThreadPriority:0.2]; //属性2:优先级
[thread1 start];
NSThread *thread2 = [[NSThread alloc] initWithTarget:self selector:@selector(runNSThread) object:nil];
[thread2 setName:@"Name_thread2"];
[thread2 setThreadPriority:0.5];
[thread2 start];
// 2.通过detachNewThreadSelector方式创建并执行线程(不用创建线程对象,是静态方法)
// [NSThread detachNewThreadSelector:@selector(runNSThread) toTarget:self withObject:nil];
// 3.通过performSelectorInBackGround方式创建线程(NSObject的方法)
// [self performSelectorInBackground:@selector(runNSThread) withObject:nil];
}
- (void)runNSThread {
NSLog(@"我在NSThread子线程 - %@", [NSThread currentThread].name);
for (int i=0; i<5; i++) {
NSLog(@"%d", i);
sleep(1);
if (i == 4) {
[self performSelectorOnMainThread:@selector(runMainThread) withObject:nil waitUntilDone:YES];
}
}
}
- (void)runMainThread {
NSLog(@"回到NSThread主线程执行");
}
#pragma mark - GCD Test
- (void)clickGCDBtn {
NSLog(@"我在GCD主线程");
// dispatch_async(dispatch_get_global_queue(0, 0), ^{
// //执行耗时任务
// NSLog(@"start task 1");
// [NSThread sleepForTimeInterval:3.0];
// dispatch_async(dispatch_get_main_queue(), ^{
// //回到主线程,刷新UI
// NSLog(@"刷新UI");
// });
// });
// dispatch_get_global_queue(long identifier, unsigned long flags); 第一个参数设置优先级
// #define DISPATCH_QUEUE_PRIORITY_HIGH 2
// #define DISPATCH_QUEUE_PRIORITY_DEFAULT 0
// #define DISPATCH_QUEUE_PRIORITY_LOW (-2)
// dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0), ^{
// NSLog(@"start task 1");
// [NSThread sleepForTimeInterval:3.0];
// NSLog(@"end task 1");
// });
//
// dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
// NSLog(@"start task 2");
// [NSThread sleepForTimeInterval:3.0];
// NSLog(@"end task 2");
// });
//
// dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
// NSLog(@"start task 3");
// [NSThread sleepForTimeInterval:3.0];
// NSLog(@"end task 3");
// });
// dispatch_queue_t queue = dispatch_queue_create("com.test.gcd.queue", DISPATCH_QUEUE_CONCURRENT);
// dispatch_async(queue, ^{
// NSLog(@"start task 1");
// [NSThread sleepForTimeInterval:3.0];
// NSLog(@"end task 1");
// });
// dispatch_async(queue, ^{
// NSLog(@"start task 2");
// [NSThread sleepForTimeInterval:3.0];
// NSLog(@"end task 2");
// });
// dispatch_async(queue, ^{
// NSLog(@"start task 3");
// [NSThread sleepForTimeInterval:3.0];
// NSLog(@"end task 3");
// });
dispatch_queue_t queue = dispatch_queue_create("com.test.gcd.group", DISPATCH_QUEUE_CONCURRENT);
dispatch_group_t group = dispatch_group_create();
// dispatch_group_async(group, queue, ^{
// NSLog(@"start task 1");
// [NSThread sleepForTimeInterval:3.0];
// NSLog(@"end task 1");
// });
// dispatch_group_async(group, queue, ^{
// NSLog(@"start task 2");
// [NSThread sleepForTimeInterval:3.0];
// NSLog(@"end task 2");
// });
// dispatch_group_async(group, queue, ^{
// NSLog(@"start task 3");
// [NSThread sleepForTimeInterval:3.0];
// NSLog(@"end task 3");
// });
// dispatch_group_async(group, queue, ^{ //两个异步请求
// //group应执行同步的代码
// [self sendRequest1:^{
// NSLog(@"request1 done");
// }];
// });
// dispatch_group_async(group, queue, ^{
// [self sendRequest2:^{
// NSLog(@"request2 done");
// }];
// });
dispatch_group_enter(group); //group执行异步代码
[self sendRequest1:^{
NSLog(@"request1 done");
dispatch_group_leave(group);
}];
dispatch_group_enter(group);
[self sendRequest2:^{
NSLog(@"request1 done");
dispatch_group_leave(group);
}];
dispatch_group_notify(group, queue, ^{
NSLog(@"all tasks over");
dispatch_async(dispatch_get_main_queue(), ^{
NSLog(@"回到主线程,刷新UI");
});
});
}
- (void)sendRequest1:(void(^)(void))block {
dispatch_async(dispatch_get_global_queue(0, 0), ^{
NSLog(@"start task sendRequest1");
[NSThread sleepForTimeInterval:3.0];
NSLog(@"end task sendRequest1");
dispatch_async(dispatch_get_main_queue(), ^{
if (block) {
block();
}
});
});
}
- (void)sendRequest2:(void(^)(void))block {
dispatch_async(dispatch_get_global_queue(0, 0), ^{
NSLog(@"start task sendRequest2");
[NSThread sleepForTimeInterval:3.0];
NSLog(@"end task sendReques2");
dispatch_async(dispatch_get_main_queue(), ^{
if (block) {
block();
}
});
});
}
#pragma mark - Single Test
- (void)clickSingleBtn {
// TestSingle *single = [TestSingle instance];
// NSLog(@"%@", single);
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
NSLog(@"单例模式 - 只执行一次");
});
}
#pragma mark - After Test
- (void)clickAfterBtn {
NSLog(@"--- begin ---");
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
NSLog(@"delay excute"); //延迟执行
});
}
#pragma mark - NSOperation Test
- (void)clickNSOperationBtn {
NSLog(@"我在NSOperation主线程");
// 1.NSInvocationOperation(同步的,系统提供的封装好的NSOperation子类的实现方式)
// dispatch_async(dispatch_get_global_queue(0, 0), ^{
// NSLog(@"我在invationOperation分线程");
// NSInvocationOperation *invocationOperation = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(invocationAciton) object:nil];
// [invocationOperation start];
// NSLog(@"NSInvocationOperation线程同步加载,非异步加载");
// });
//2.NSBlockOperation(同步的,系统提供的封装好的NSOperation子类的实现方式)
// NSBlockOperation *blockOperation = [NSBlockOperation blockOperationWithBlock:^{
// for (int i=0; i<3; i++) {
// NSLog(@"invocation %d", i);
// [NSThread sleepForTimeInterval:1.0];
// }
// }];
// [blockOperation start];
if (!self.operationQueue) {
self.operationQueue = [[NSOperationQueue alloc] init];
}
[self.operationQueue setMaxConcurrentOperationCount:3]; //设置最大线程数量
// 若要用NSOperationQueue,则不使用[blockOperation start]; 使用下面的方法,将blockOperation加入到队列中
// [self.operationQueue addOperation:blockOperation];
//3.自定义NSOperation
CustomOperation *customOperation = [[CustomOperation alloc] initWithName:@"CustomOperation"];
CustomOperation *otherOperation1 = [[CustomOperation alloc] initWithName:@"OtherOperation1"];
CustomOperation *otherOperation2 = [[CustomOperation alloc] initWithName:@"OtherOperation2"];
//依赖关系 custom依赖other1,other1依赖other2,即使最大线程数设置为所有线程的总和,但也会根据依赖关系,最顶端的依赖other2先执行结束再依次依赖传递的执行,即:other2->other1->custom
[customOperation addDependency:otherOperation1];
[otherOperation1 addDependency:otherOperation2];
[self.operationQueue addOperation:customOperation];
[self.operationQueue addOperation:otherOperation1];
[self.operationQueue addOperation:otherOperation2];
NSLog(@"NSOperationQueue异步加载,执行本行代码的同时在执行子线程");
}
- (void)invocationAciton {
for (int i=0; i<3; i++) {
NSLog(@"invocation %d", i);
[NSThread sleepForTimeInterval:1.0];
}
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
@end
TicketManager.h:
//
// TicketManager.h
// TestThread
//
// Created by APACHE on 2017/10/22.
// Copyright © 2017年 APACHE. All rights reserved.
//
#import <Foundation/Foundation.h>
@interface TicketManager : NSObject
- (void)startToSale;
@end
TicketManager.m:
//
// TicketManager.m
// TestThread
//
// Created by APACHE on 2017/10/22.
// Copyright © 2017年 APACHE. All rights reserved.
//
#import "TicketManager.h"
#define Total 20
@interface TicketManager()
@property int tickets;
@property int saleCount;
@property (nonatomic, strong) NSThread *threadBJ;
@property (nonatomic, strong) NSThread *threadSH;
@property (nonatomic, strong) NSCondition *ticketCondition;
@end
@implementation TicketManager
- (instancetype)init {
self = [super init];
if (self) {
self.ticketCondition = [[NSCondition alloc] init];
self.tickets = Total;
self.threadBJ = [[NSThread alloc] initWithTarget:self selector:@selector(sale) object:nil];
[self.threadBJ setName:@"BJ_NSThread"];
[self.threadBJ setThreadPriority:0.9];
self.threadSH = [[NSThread alloc] initWithTarget:self selector:@selector(sale) object:nil];
[self.threadSH setName:@"SH_NSThread"];
[self.threadSH setThreadPriority:0.1];
}
return self;
}
- (void)sale {
while (1) {
// @synchronized (self) { //加锁方式1
[self.ticketCondition lock]; //加锁方式2
if (self.tickets > 0) {
[NSThread sleepForTimeInterval:0.5]; //时间间隔
self.tickets --;
self.saleCount = Total - self.tickets;
NSLog(@"%@ - 当前余票: %d, 售出: %d", [NSThread currentThread].name, self.tickets, self.saleCount);
}
[self.ticketCondition unlock];
// }
}
}
- (void)startToSale {
[self.threadBJ start];
[self.threadSH start];
}
@end
TestSingle.h:
//
// TestSingle.h
// TestThread
//
// Created by APACHE on 2017/10/22.
// Copyright © 2017年 APACHE. All rights reserved.
//
#import <Foundation/Foundation.h>
@interface TestSingle : NSObject
+ (instancetype)instance;
@end
TestSingle.m:
//
// TestSingle.m
// TestThread
//
// Created by APACHE on 2017/10/22.
// Copyright © 2017年 APACHE. All rights reserved.
//
#import "TestSingle.h"
@implementation TestSingle
+ (instancetype)instance {
static dispatch_once_t onceToken;
static TestSingle *ins = nil;
dispatch_once(&onceToken, ^{
NSLog(@"Init the TestSingle");
ins = [[TestSingle alloc] init];
});
return ins;
}
@end
CustomOperation.h:
//
// CustomOperation.h
// TestThread
//
// Created by APACHE on 2017/10/22.
// Copyright © 2017年 APACHE. All rights reserved.
//
#import <Foundation/Foundation.h>
@interface CustomOperation : NSOperation
- (instancetype)initWithName:(NSString *)name;
@end
CustomOperation.m:
//
// CustomOperation.m
// TestThread
//
// Created by APACHE on 2017/10/22.
// Copyright © 2017年 APACHE. All rights reserved.
//
#import "CustomOperation.h"
@interface CustomOperation()
@property (nonatomic, copy) NSString *operationName;
@property BOOL over;
@end
@implementation CustomOperation
- (instancetype)initWithName:(NSString *)name {
self = [super init];
if (self) {
self.operationName = name;
}
return self;
}
- (void)main { //重写main方法,耗时操作在这里执行
// for (int i=0; i<3; i++) {
// NSLog(@"%@ - %d", self.operationName, i);
// [NSThread sleepForTimeInterval:1.0];
// }
dispatch_async(dispatch_get_global_queue(0, 0), ^{ //不满足依赖关系,因为是异步请求,main方法很快就执行结束了,operation就结束了,相应的依赖关系就结束了。解决办法用RunLoop⬇️
[NSThread sleepForTimeInterval:1.0];
if (self.cancelled) {
return ;
}
else {
NSLog(@"%@", self.operationName);
self.over = YES;
}
});
while (!self.over && !self.cancelled) {
[[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]]; //程序启动,主线程会有一个RunLoop,用来循环监听状态。除非over或cancelled,才会结束。这样就满足依赖关系。
}
}
@end
加油!