多线程使用

  • 讲NSthread目的就是说明白线程的生命周期,后面的GCD和更高级的NSOperation的生命周期都是自动管理的了。

  (一)PThread(user/include跨平台的可以百科百度搜索):

    导入<pthread.h>,pthread_create四参数:1.线程,2.线程相关NULL,3.方法,4.方法参数。

    demo:

// pthread 演练

// pthread 演练
- (void)pthreadDemo {
    
    /**
     几乎所有跨平台的框架都可以访问一个网站:baike.baidu.com
     
     返回值
     
     若线程创建成功,则返回0。若线程创建失败,则返回出错编号
     
     - 非零即真,但是很多 C 语言的框架,成功是0,失败是编号
     - 因为成功只有一个,失败有很多原因
     
     参数
     
     第一个参数:为指向线程标识符的指针
        C 语言中,没有对象,是通过结构体来实现的,不需要使用 *
        如果使用 C 语言的框架,定义类型,通常是 _t 或者 Ref 结尾
     第二个参数:用来设置线程属性
     第三个参数:是线程运行函数的起始地址
        函数名就是指向函数在内存中的地址
        类似的一个概念:数组名是指向数组第一个元素的地址
     
     最后一个参数:是运行函数的参数
     
     在 C 语言中 void *(指向任何地址的指针) 和 OC 中的 id(万能指针) 是等价的
     
     格式 void * (*) (void *)
     返回值 (*函数指针) (参数)
     
     ====== 
     `桥接` __bridge
     
     - 是 ARC 开发时,用于 OC 对象和 C 语言对象转换时的标记
     - ARC 开发的时候,编译器会根据代码结构,自动添加 retain/release/autorelease
     - ARC 不负责 C 语言的内存管理,如果碰到 C 语言的框架,如果出现 retain/new/copy/create...
        程序员需要自己 release
     - 在 OC 对象和 C 语言的指针进行转换时,需要使用 __bridge,表示什么特殊处理都不做
     - 后续课程会涉及桥接的所有权转换
     
     - 标记的作用就是告诉编译器如何处理内存
     
     > 提问:MRC 开发,需要 __bridge?因为 MRC 中所有的都是程序员来负责的!
     > 提示:__bridge可以利用 Xcode 智能提示
     */

    // 创建一个线程,执行指定的方法
    // 线程代号
    pthread_t threadId = NULL;
    
    id str = @"hello";
    
    int result = pthread_create(&threadId, NULL, demo, (__bridge void *)(str));
    
    if (result == 0) {
        NSLog(@"OK");
    } else {
        NSLog(@"error");
    }
}

// 由线程执行的函数
void * demo(void * param) {
    
    NSString *str = (__bridge NSString *)(param);
    
    // [NSThread currentThread]适用于所有多线程技术
    NSLog(@"%@ - %@", [NSThread currentThread], str);
    
    return NULL;
}

 

(二)NSThread:是OC的。创建有三种方法:

(1)对象方法: alloc initwithTarget

(2)类方法: detatchNewThreadSelector

(3)NSObject分类方法:performSlelctorInBackground:意味着任意OC对象都可以调用

  • 在使用exit时候,要考虑是否有C语言框架资源,这种资源需要手动释放。

  • exit会取消当前线程。所以不要在主线程exit。

  •  

  • 取消线程的方法:

  1. 内部:exit

  2. 外部:cancel,设置线程状态为取消,然后从内部判断(在关键节点位置增加判断,否则不会终止,需要判断)。is.canceled ->然后执行。

  • 线程属性:

  1. name:调用堆栈调试时候可以知道线程名称。用于排错。

  2. stackSize:线程开多大。目前主线程子线程都是512K。可以修改。最小16K,必须是4K的整数倍。

  3. 3.threadPriority 0-1,0最高,默认0.5.优先级高只是CPU调度频率高,不是说先调用。一般不修改。

  • 多线程资源抢夺:进程和进程之间内存是受保护的。但是一个进程的不同线程资源是共享的。在一起的。用到互斥锁。应该让锁定范围尽量小。越大越慢,锁的是{}里面的代码。要锁住共享资源的读和写部分。而且原子锁的self可以是任意OC对象,但是锁一定要全局。

  • 原子属性和非原子属性:

    1. 原子属性:atomic。线程安全的。单写多读,同一时间只有一个线程可以写入,但是读取可以多读,可能会出现脏数据。。可能会出现脏数据。你我读都是15,但是写是安全的,读可能是错误的。但是原子属性性能比互斥锁性能高。atomic的锁叫自旋锁。

    2. 非原子属性:nonatomic。线程不安全。

    3. 原子属性锁效率比互斥锁高很多。只要用锁就有性能消耗。

    4. 最好不用锁。属性都用nonatomic,几乎所有的UIKit都是线程不安全的,所有的UI更新都在主线程。为了性能。

posted @ 2016-08-25 15:43  三更小新  阅读(71)  评论(0编辑  收藏  举报