多线程

2018-03-19  本文已影响11人  iChuck

线程与进程的区别和联系?

oc 中的几种多线程

几种多线程技术.png

多线程使用

// 耗时操作
for (int i = 0; i < 500; i++) {
        //[NSThread currentThread] 判断是否是在主线程
        // number == 1 说明是主线程  != 1 就是其他线程
        //NSLog 用来做调试
        NSLog(@"%d%@",i,[NSThread currentThread]); 
    }

pthread

- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
    
    [self pthreadDemo];
}

- (void)pthreadDemo{
    
    /**
     pthread 是属于 POSIX 多线程开发框架
     
     参数:
     1.指向线程代号的指针
     2.线程的属性
     3.指向函数的指针
     4.传递给该函数的参数
     
     返回值
     - 如果是0,标示正确
     - 如果非0,标示错误代码
     
     void *   (*)      (void *)
     返回值   (函数指针)  (参数)
     void *  和OC中的  id 是等价的!
     
     
     */
    NSString * str = @"hello Hank";
    pthread_t threadId;
    /**
     - 在 ARC 开发中,如果涉及到和C语言中的相同的数据类型进行转换,需要使用 __bridge "桥接"
     - 在 MRC 不需要
     */
    
    int result = pthread_create(&threadId, NULL, &demo, (__bridge  void *)(str));
   
    if (result == 0) {
        NSLog(@"OK");
    }else{
        NSLog(@"error %d",result);
    }
    
    
    
}

void * demo(void * param){
    NSLog(@"%@ %@", [NSThread currentThread], param);
    
    return NULL;
}

NSThread

// 实例方法
- (void)threadDemo1{
    
    NSLog(@"A-------------");
    
    // 创建一个NSThread
    NSThread *thread = [[NSThread alloc] initWithTarget:self selector:@selector(demo:) object:@"Thread"];
    //启动线程
    [thread start];
    for (int i = 0; i < 10; i++) {
        NSLog(@"%d",i);
    }
    NSLog(@"B-------------");
}

// 类方法
- (void)threadDemo2{
    //1
    NSLog(@"A---%@",[NSThread currentThread]);
    
    //detach ==> 分离
    [NSThread detachNewThreadSelector:@selector(demo:) toTarget:self withObject:@"Detach"];
    //1
    NSLog(@"B---%@",[NSThread currentThread]);
}

// NSObject 分类方法,全局使用
- (void)threadDemo3{
    
    //1
    NSLog(@"A---%@",[NSThread currentThread]);
    
    // InBackground 就是在后台(子线程)运行!!
    // 是NSObject的分类 意味着所有的基础NSObject的都可以使用这个方法
    // 非常方便.不用NSThread对象
    [self performSelectorInBackground:@selector(demo:) withObject:@"background"];
    //1
    NSLog(@"B---%@",[NSThread currentThread]);
}

- (void)demo:(id)obj{
    for (int i = 0; i < 2; i++) {
        // !=1
        NSLog(@"C---------------%@",[NSThread currentThread]);
    }
}

//开启主线程!!
[[NSThread mainThread] start];

// exit 杀掉主线程,但是 App 不会挂掉!
[NSThread exit];

// 休眠状态
[NSThread sleepForTimeInterval:1.0];
    NSThread * t = [[NSThread alloc]initWithTarget:self selector:@selector(demo) object:nil];
    //在大型的商业项目中,通常希望程序在崩溃的时候,能够获取到程序准确的所以在的线程!
    t.name = @"Thread A";
    //优先级 从 0.0 -- 1.0  默认值 0.5
    /**  优先级翻转
     优先级 只是保证 CPU 调度的可能性会高!
     
     多线程目的:将耗时操作放在后台,不阻塞UI线程!
     建议:在开发的时候,不要修改优先级
     
     在多线程开发中,不要相信一次的运行结果!!
     */
    t.threadPriority = 0.1;
    [t start];
    
    NSThread * t1 = [[NSThread alloc]initWithTarget:self selector:@selector(demo) object:nil];
    //在大型的商业项目中,通常希望程序在崩溃的时候,能够获取到程序准确的所以在的线程!
    t1.name = @"Thread B";
    t1.threadPriority = 1.0;
    [t1 start];
/** 票 */
@property(assign,nonatomic)int tickets;
/** 锁  */
@property(nonatomic,strong)NSObject * lockObjc;


- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
    self.tickets = 20;
    NSThread * t1 = [[NSThread alloc]initWithTarget:self selector:@selector(saleTickets) object:nil];
    t1.name = @"售票员 A";
    [t1 start];
    
    NSThread * t2 = [[NSThread alloc]initWithTarget:self selector:@selector(saleTickets) object:nil];
    t2.name = @"售票员 B";
    [t2 start];
    
//    [self saleTickets];
    
}


- (void)saleTickets{
    while (YES) {
        //0.买票要1秒钟
        [NSThread sleepForTimeInterval:1.0];
        //参数:就是能够加锁的任意 NSOjbect 对象
        //局部变量: 每个线程单独拥有的,无法锁住!!
        //注意: 锁一定要是所有线程共享的对象!!
//        NSObject * lockObj = [[NSObject alloc]init];
        @synchronized (self.lockObjc) {
        //互斥锁 - 保证锁内的代码,同一时间,只有一条线程能够执行!!
        //互斥锁它的范围,应该尽量小,锁范围越大,效率越低!
            //1.还有没有票
            if (self.tickets > 0) {
                //2.卖出一张票
                self.tickets --;
                NSLog(@"剩下 %d 张票 --- %@",self.tickets,[NSThread currentThread]);
            }else{
                NSLog(@"没有票了 %@",[NSThread currentThread]);
                break;
            }
        }
    }
    
}

GCD(Grand Centeral Dispatch)

// 同来同步的方式执行任务
void dispatch_sync(dispatch_queue_t queue, DISPATCH_NOESCAPE dispatch_block_t block);

// 用来异步的方式执行任务
void dispatch_async(dispatch_queue_t queue, dispatch_block_t block);

// 在前面的任务执行结束它才执行,而且他后面的任务等他执行完成之后才会执行
void dispatch_barrier_sync(dispatch_queue_t queue,
        DISPATCH_NOESCAPE dispatch_block_t block);
        
void dispatch_barrier_async(dispatch_queue_t queue,
        DISPATCH_NOESCAPE dispatch_block_t block);

dispatch_queue_t
dispatch_queue_create(const char *_Nullable label, // 队列名称
        dispatch_queue_attr_t _Nullable attr); // 队列类型
        
// 创建并发队列   
dispatch_queue_t queue = dispatch_queue_create("com.Mr.Right", DISPATCH_QUEUE_CONCURRENT);

// gcd 提供了默认全局并发队列
dispatch_queue_t
dispatch_get_global_queue(long identifier, //队列的优先级
unsigned long flags); // 暂时无用,默认0

dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);

// 优先级
#define DISPATCH_QUEUE_PRIORITY_HIGH 2   // 高
#define DISPATCH_QUEUE_PRIORITY_DEFAULT 0 // 默认(中)
#define DISPATCH_QUEUE_PRIORITY_LOW (-2) // 低
#define DISPATCH_QUEUE_PRIORITY_BACKGROUND INT16_MIN // 后台
// 创建串行队列(队列类型传 NULL 或者 DISPATCH_QUEUE_SERIAL)
dispatch_queue_t queue = dispatch_queue_create("com.Mr.Right", DISPATCH_QUEUE_SERIAL);

/**
使用主队列(跟主线程相关联的队列)
主队列是 GCD 自带的一种特殊的串行队列
放在主队列中的任务,都会放到主线程中执行
使用 dispatch_get_main_queue()获取主队列
*/

dispatch_queue_t queue = dispatch_get_main_queue();
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
       
        // 执行耗时操作
        dispatch_async(dispatch_get_main_queue(), ^{
            // 回到主线程操作 UI
        });
    });
// NSObject 的方法 2s 后执行 demo 方法
[self performSelector:@selector(demo) withObject:nil afterDelay:2.0];

// GCD  函数, gcd时间更精确
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
    // 2s 后执行代码
});

// NSTimer 
[NSTimer scheduledTimerWithTimeInterval:2.0f target:self selector:@selector(demo) userInfo:nil repeats:NO];
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
    // 在程序执行期间只会被执行一次(默认是线程安全的)
});
dispatch_apply(10, dispatch_get_global_queue(0, 0), ^(size_t index) {
   // 执行10次代码,Index 不确定
});
// 在网络请求的时候,分别异步执行2个耗时操作,其两个耗时操作执行完毕后再回到主线程执行操作
// 可以使用队列组

dispatch_group_t group = dispatch_group_create();
    
dispatch_group_async(group, dispatch_get_global_queue(0, 0), ^{
    // 执行第一个耗时操作
});
    
dispatch_group_async(group, dispatch_get_global_queue(0, 0), ^{
    // 执行第二个耗时操作
});
    
dispatch_group_notify(group, dispatch_get_main_queue(), ^{
   // 任务完成执行 UI 操作
});

NSOperaction

// 创建 NSInvocationOperation
- (nullable instancetype)initWithTarget:(id)target selector:(SEL)sel object:(nullable id)arg;
// 调用 start 方法开始执行操作
- (void)start;

// 一旦执行操作,就会执行 target 的 sel 方法
// 注意:默认情况下,调用了 start 方法后并不会开一条新线程去执行操作,而是当前线程同步执行操作;
// 只有将 NSOperation 放到一个 NSOperationQueue 中,才会执行异步操作
// 创建 NSBlockOperation 对象
+ (instancetype)blockOperationWithBlock:(void (^)(void))block;
// 添加更多操作
- (void)addExecutionBlock:(void (^)(void))block;

注意:只要 NSBlockOperation 封装的操作数大于1,就会执行异步操作

我们项目中为什么多线程用 GCD 而不用 NSOperation?你有没有发现国外的大牛他们多线程都是用 NSOperation?你能告诉我他们这样做的理由吗?

  1. NSOperation 是用 GCD 构建封装的,是 GCD 的高级抽象。
  2. GCD 仅仅支持 FIFO 队列,而 NSOperationQueue 中的队列可以被重新设置优先级,从而实现不同操作的执行顺序调整。GCD 不支持异步操作之间的依赖关系设置。如果某个操作的依赖另一个操作的数据(生产者-消费者模型),使用 NSOperationQueue 能够按照正确的顺序执行操作。GCD 则没有內建依赖关系支持。
  3. NSOperationQueue 支持 KVO,意味着我们可以观察任务执行的状态。
  4. GCD 更接近底层,而 NSOperationQUeue 则更高级抽象,所以 GCD 在追求性能的底层操作来说,是速度最快的。

详解 GCD 死锁

iOS 多线程底层实现原理

多线程安全问题?何为线程同步,如何实现?分线程回调主线程的方法是什么?有什么作用?

使用 atomic 一定线程安全吗?

谈谈你对多线程开发的理解(多线程的好处,多线程的作用)

上一篇 下一篇

猜你喜欢

热点阅读