线程

NSThread

一个NSThread对象就代表一条线程

创建启动

以下线程启动就会执行run方法
1、
创建线程并手动启动

NSThread *thread = [[NSThread alloc] initWithTarget:self selector:@selector(run) object:nil];
[thread start];

2、
创建线程后自动启动线程

[NSThread detachNewThreadSelector:@selector(run) toTarget:self withObject:nil];

3、
隐式创建并启动线程

[self performSelectorInBackground:@selector(run) withObject:nil];

线程常用方法

主线程

+ (NSThread *)mainThread; // 获得主线程
- (BOOL)isMainThread; // 是否为主线程
+ (BOOL)isMainThread; // 是否为主线程

当前线程

NSThread *current = [NSThread currentThread];

优先级
调度优先级的取值范围是0.0 ~ 1.0,默认0.5,值越大,优先级越高

+ (double)threadPriority;
+ (BOOL)setThreadPriority:(double)p;
- (double)threadPriority;
- (BOOL)setThreadPriority:(double)p;

线程名

- (void)setName:(NSString *)n; - (NSString *)name;

线程状态


启动线程

- (void)start; 

阻塞(暂停)线程

+ (void)sleepUntilDate:(NSDate *)date;
+ (void)sleepForTimeInterval:(NSTimeInterval)ti;

强制停止线程

+ (void)exit;

注意:一旦线程停止(死亡)了,就不能再次开启任务

互斥锁

// 锁定1份代码只用1把锁,用多把锁是无效的
// 常用锁对象为self
@synchronized(锁对象) { // 需要锁定的代码 }

互斥锁的优缺点

优点:能有效防止因多线程抢夺资源造成的数据安全问题
缺点:需要消耗大量的CPU资源

互斥锁的使用前提
多条线程抢夺同一块资源

原子和非原子属性

OC在定义属性时有nonatomic和atomic两种选择

  1. atomic:原子属性,为setter方法加锁(默认就是atomic)
  2. nonatomic:非原子属性,不会为setter方法加锁

atomic加锁原理

@property (assign, atomic) int age;
 - (void)setAge:(int)age
{
    @synchronized(self) {
        _age = age;
    }
}

nonatomic和atomic对比
- atomic:线程安全,需要消耗大量的资源
- nonatomic:非线程安全,适合内存小的移动设备

建议:
所有属性都声明为nonatomic
尽量避免多线程抢夺同一块资源
尽量将加锁、资源抢夺的业务逻辑交给服务器端处理,减小移动客户端的压力

线程间通信

// 在主线程中执行方法
- (void)performSelectorOnMainThread:(SEL)aSelector withObject:(id)arg waitUntilDone:(BOOL)wait; // 在对应线程中执行方法 - (void)performSelector:(SEL)aSelector onThread:(NSThread *)thr withObject:(id)arg waitUntilDone:(BOOL)wait;

GCD

同步的方式
在当前线程中执行,不会开启新线程

// queue 队列
// block 任务
void dispatch_sync(dispatch_queue_t queue, dispatch_block_t block);

异步的方式
在另一条线程中执行,可以开启新线程

// queue 队列
// block 任务
void dispatch_async(dispatch_queue_t queue, dispatch_block_t block);

队列类型:

并发队列
可让多个任务并发执行,即同时开启多个线程执行任务
要实现并发功能,只能使并发队列在异步函数下有效。
串行队列
让任务按顺序一个一个执行。

获取全局并发队列

// identifier 优先级
// DISPATCH_QUEUE_PRIORITY_HIGH 2
// DISPATCH_QUEUE_PRIORITY_DEFAULT 0
// DISPATCH_QUEUE_PRIORITY_LOW -2
// DISPATCH_QUEUE_PRIORITY_BACKGROUND 后台
dispatch_queue_t dispatch_get_global_queue(long identifier, unsigned long flags);

// 事例
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);

获取串行队列

// 1 创建队列
// label 队列名
dispatch_queue_t dispatch_queue_create(const char *label, dispatch_queue_attr_t attr);
// 需要对创建的线程进行释放(MRC)
void dispatch_release(dispatch_object_t object);

// 2 获取主队列
// 主队列是GCD自带的一种特殊的串行队列,放在主队列中的任务都会放到主线程中执行
dispatch_queue_t dispatch_get_main_queue(void);

关于释放的总结

1、 凡是函数名种带有create\copy\new\retain等字眼, 都需要在不需要使用这个数据的时候进行release
2、GCD的数据类型在ARC环境下不需要再做release
3、CF(Core Foundation)的数据类型在ARC环境下还是需要再做release

队列执行效果

线程间通信

dispatch_async(
dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
    // 执行耗时的异步操作...
      dispatch_async(dispatch_get_main_queue(), ^{
        // 回到主线程,执行UI刷新操作
        });
});

执行一次代码

static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
    // 只执行1次的代码(这里面默认是线程安全的)
});

队列组

使用场所:

  1. 分别异步执行2个耗时的操作
  2. 等2个异步操作都执行完毕后,再回到主线程执行操作
dispatch_group_t group =  dispatch_group_create();
dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
    // 执行1个耗时的异步操作
});
dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
    // 执行1个耗时的异步操作
});
dispatch_group_notify(group, dispatch_get_main_queue(), ^{
    // 等前面的group中异步操作都执行完毕后,回到主线程...
});

NSOperation

实现步骤

你可能感兴趣的:(线程)