前言
本系列文章列表
为什么会写这系列文章?
一、多线程概述
- 进程与线程:线程是进程内假想的持有CPU使用权的执行单位。一个程序启动便有一个进程,一个进程可以有多个线程,但只有一个主线程。
- 父线程与子线程:创建线程时,创建方的线程为父线程,被创建方的线程为子线程。父线程可以等到子线程执行完毕后与其会和;也可以切断它们的关系让它们分离(比如NSThread)。
- 共享变量:多个线程都能访问的变量称为共享变量,因为地址空间是共享的,所以理论上所有的内存区域都是共享变量。如果不同线程同时访问同一个变量,这个变量就是不安全的,我们需要通过一些骚操作来实现线程间互斥(比如后面会讲的各种锁)
- 并发与并行:并行一定并发,并发不一定并行。在单核设备上,CPU通过频繁的切换上下文来运行不同的线程,速度足够快以至于我们看起来它是‘并行’处理的,然而我们只能说这种情况是并发而非并行。例如:你和两个人一起百米赛跑,你一直在不停的切换跑道,而其他两人就在自己的跑道上,最终,你们三人同时到达了终点。我们把跑道看做任务,那么,其他两人就是并行执行任务的,而你只能的说是并发执行任务(嗯,这个例子还行)。
- 多线程底层:多线程的底层实现机制是基于Mach的,Mach是第一个以多线程处理任务的系统,但是Mach级别的线程是相互独立的,线程之间不能通讯。
二、NSThread
NSThread是我们用来创建并管理线程的类,它的使用很简单,暴露的接口也很有限。通常情况下,我们更多的是使用GCD和NSOperation来管理任务,而不用去管理线程的布局,因为毕竟CPU和GCD已经做得很好了。
直接贴上常用的API
//获取当前线程,请注意,这里是类的属性
@property (class, readonly, strong) NSThread *currentThread;
//这两个方法是便利构造类方法,用它们创建的线程会立马执行不用start,上面个是ios10.0过后的方法,使用的小伙伴要注意了
+ (void)detachNewThreadWithBlock:(void (^)(void))block API_AVAILABLE(macosx(10.12), ios(10.0), watchos(3.0), tvos(10.0));
+ (void)detachNewThreadSelector:(SEL)selector toTarget:(id)target withObject:(nullable id)argument;
//当前是否是多线程
+ (BOOL)isMultiThreaded;
//当前线程睡眠,参数看得懂吧
+ (void)sleepUntilDate:(NSDate *)date;
+ (void)sleepForTimeInterval:(NSTimeInterval)ti;
//退出当前线程
+ (void)exit;
//当前线程的优先级
+ (double)threadPriority;
//设置当前线程的优先级
+ (BOOL)setThreadPriority:(double)p;
//线程优先级
@property double threadPriority API_AVAILABLE(macos(10.6), ios(4.0), watchos(2.0), tvos(9.0)); // To be deprecated; use qualityOfService below
//线程名字
@property (nullable, copy) NSString *name API_AVAILABLE(macos(10.5), ios(2.0), watchos(2.0), tvos(9.0));
//是否是主线程,一个是类方法,一个是实例方法
@property (readonly) BOOL isMainThread API_AVAILABLE(macos(10.5), ios(2.0), watchos(2.0), tvos(9.0));
@property (class, readonly) BOOL isMainThread API_AVAILABLE(macos(10.5), ios(2.0), watchos(2.0), tvos(9.0)); // reports whether current thread is main
//获取主线程
@property (class, readonly, strong) NSThread *mainThread API_AVAILABLE(macos(10.5), ios(2.0), watchos(2.0), tvos(9.0));
//初始化方法,没什么可说的
- (instancetype)init API_AVAILABLE(macos(10.5), ios(2.0), watchos(2.0), tvos(9.0)) NS_DESIGNATED_INITIALIZER;
- (instancetype)initWithTarget:(id)target selector:(SEL)selector object:(nullable id)argument API_AVAILABLE(macos(10.5), ios(2.0), watchos(2.0), tvos(9.0));
- (instancetype)initWithBlock:(void (^)(void))block API_AVAILABLE(macosx(10.12), ios(10.0), watchos(3.0), tvos(10.0));
//正在执行
@property (readonly, getter=isExecuting) BOOL executing API_AVAILABLE(macos(10.5), ios(2.0), watchos(2.0), tvos(9.0));
//已经完成
@property (readonly, getter=isFinished) BOOL finished API_AVAILABLE(macos(10.5), ios(2.0), watchos(2.0), tvos(9.0));
//已经取消
@property (readonly, getter=isCancelled) BOOL cancelled API_AVAILABLE(macos(10.5), ios(2.0), watchos(2.0), tvos(9.0));
//手动取消
- (void)cancel API_AVAILABLE(macos(10.5), ios(2.0), watchos(2.0), tvos(9.0));
//手动开始
- (void)start API_AVAILABLE(macos(10.5), ios(2.0), watchos(2.0), tvos(9.0));
//入口函数,如果你要重写thread就得override,当然,我这里没贴上的api重写thread应该能用到。不过重写这种骚操作基本不会用到。
- (void)main API_AVAILABLE(macos(10.5), ios(2.0), watchos(2.0), tvos(9.0)); // thread body method
下面是延展里面的Api
//几个通知,看命名就知道意思了
FOUNDATION_EXPORT NSNotificationName const NSWillBecomeMultiThreadedNotification;
FOUNDATION_EXPORT NSNotificationName const NSDidBecomeSingleThreadedNotification;
FOUNDATION_EXPORT NSNotificationName const NSThreadWillExitNotification;
@interface NSObject (NSThreadPerformAdditions)
//回到主线程执行操作的方法
- (void)performSelectorOnMainThread:(SEL)aSelector withObject:(nullable id)arg waitUntilDone:(BOOL)wait modes:(nullable NSArray<NSString *> *)array;
- (void)performSelectorOnMainThread:(SEL)aSelector withObject:(nullable id)arg waitUntilDone:(BOOL)wait;
// equivalent to the first method with kCFRunLoopCommonModes
- (void)performSelector:(SEL)aSelector onThread:(NSThread *)thr withObject:(nullable id)arg waitUntilDone:(BOOL)wait modes:(nullable NSArray<NSString *> *)array API_AVAILABLE(macos(10.5), ios(2.0), watchos(2.0), tvos(9.0));
- (void)performSelector:(SEL)aSelector onThread:(NSThread *)thr withObject:(nullable id)arg waitUntilDone:(BOOL)wait API_AVAILABLE(macos(10.5), ios(2.0), watchos(2.0), tvos(9.0));
// equivalent to the first method with kCFRunLoopCommonModes
//在后台运行,调用该方法会自动创建一个异步线程
- (void)performSelectorInBackground:(SEL)aSelector withObject:(nullable id)arg API_AVAILABLE(macos(10.5), ios(2.0), watchos(2.0), tvos(9.0));
API还是很简单得,实际开发中,对于NSThread的使用还是比较多,主要就是获取当前线程currentThread
,线程休眠sleepForTimeInterval:
,判断是否在主线程isMainThread
,以及回到主线程performSelector: onThread:
系列方法等等。
网友评论