十九、多线程

2020-11-02  本文已影响0人  Mjs

线程

进程

进程和线程的关系

  1. ⼀个进程崩溃后,在保护模式下不会对其他进程产⽣影响,但是⼀个线程崩溃整个进
    程都死掉。所以多进程要⽐多线程健壮。
  2. 进程切换时,消耗的资源⼤,效率⾼。所以涉及到频繁的切换时,使⽤线程要好于进
    程。同样如果要求同时进⾏并且⼜要共享某些变量的并发操作,只能⽤线程不能⽤进程
  3. 执⾏过程:每个独⽴的进程有⼀个程序运⾏的⼊⼝、顺序执⾏序列和程序⼊⼝。但是
    线程不能独⽴执⾏,必须依存在应⽤程序中,由应⽤程序提供多个线程执⾏控制。
  4. 线程是处理器调度的基本单位,但是进程不是。
  5. 线程没有地址空间,线程包含在进程地址空间中

多线程

时间⽚的概念:

CPU在多个任务直接进⾏快速的切换,这个时间间隔就是时间⽚

多线程技术方案.png
    //0: pthread
    /**
     pthread_create 创建线程
     参数:
     1. pthread_t:要创建线程的结构体指针,通常开发的时候,如果遇到 C 语言的结构体,类型后缀 `_t / Ref` 结尾
     同时不需要 `*`
     2. 线程的属性,nil(空对象 - OC 使用的) / NULL(空地址,0 C 使用的)
     3. 线程要执行的`函数地址`
     void *: 返回类型,表示指向任意对象的指针,和 OC 中的 id 类似
     (*): 函数名
     (void *): 参数类型,void *
     4. 传递给第三个参数(函数)的`参数`
     
     返回值:C 语言框架中非常常见
     int
     0          创建线程成功!成功只有一种可能
     非 0       创建线程失败的错误码,失败有多种可能!
     */
    
    // 1: pthread
    pthread_t threadId = NULL;
    //c字符串
    char *cString = "HelloCode";
//    NSString *ocString = @"Gavin";
    //延伸到: OC--C的混编 尤其在智能家居,SDK封装
    //抛出一个问题: 在ARC需要这样操作,在MRC不需要
    // OC prethread -- 跨平台
    // 锁
    int result = pthread_create(&threadId, NULL, pthreadTest, cString);
    if (result == 0) {
        NSLog(@"成功");
    } else {
        NSLog(@"失败");
    }
    // 2: NSThread
    [NSThread detachNewThreadSelector:@selector(threadTest) toTarget:self withObject:nil];
    // 3: GCD
    dispatch_async(dispatch_get_global_queue(0, 0), ^{
        [self threadTest];
    });
    
    // 4: NSOperation
    [[[NSOperationQueue alloc] init] addOperationWithBlock:^{
        [self threadTest];
    }];
    

C与OC的桥接

优先级问题

typedef NS_ENUM(NSInteger, NSQualityOfService) {
    NSQualityOfServiceUserInteractive = 0x21,
    NSQualityOfServiceUserInitiated = 0x19,
    NSQualityOfServiceUtility = 0x11,
    NSQualityOfServiceBackground = 0x09,
    NSQualityOfServiceDefault = -1
} API_AVAILABLE(macos(10.10), ios(8.0), watchos(2.0), tvos(9.0));

并非优先级越快速度越快,还要看资源的大小和任务的复杂度

互斥锁

自旋锁是一种互斥锁的实现方式而已,相比一般的互斥锁会在等待期间放弃cpu,自旋锁(spinlock)则是不断循环并测试锁的状态,这样就一直占着cpu。

互斥锁:用于保护临界区,确保同一时间只有一个线程访问数据。对共享资源的访问,先对互斥量进行加锁,如果互斥量已经上锁,调用线程会阻塞,直到互斥量被解锁。在完成了对共享资源的访问后,要对互斥量进行解锁。

临界区:每个进程中访问临界资源的那段程序称为临界区,每次只允许一个进程进入临界区,进入后不允许其他进程进入。

自旋锁:与互斥量类似,它不是通过休眠使进程阻塞,而是在获取锁之前一直处于忙等(自旋)阻塞状态。用在以下情况:锁持有的时间短,而且线程并不希望在重新调度上花太多的成本。"原地打转"。

自旋锁与互斥锁的区别:线程在申请自旋锁的时候,线程不会被挂起,而是处于忙等的状态。

信号量:信号量是一个计数器,可以用来控制多个进程对共享资源的访问。它常作为一种锁机制,防止某进程正在访问共享资源时,其他进程也访问该资源。因此,主要作为进程间以及同一进程内不同线程之间的同步手段。

atomic与nonatomic 的区别

nonatomic ⾮原⼦属性
atomic 原⼦属性(线程安全),针对多线程设计的,默认值
保证同⼀时间只有⼀个线程能够写⼊(但是同⼀个时间多个线程都可以取值)
atomic 本身就有⼀把锁(⾃旋锁)
单写多读:单个线程写⼊,多个线程可以读取
atomic:线程安全,需要消耗⼤量的资源
nonatomic:⾮线程安全,适合内存⼩的移动设备
iOS 开发的建议
所有属性都声明为 nonatomic
尽量避免多线程抢夺同⼀块资源
尽量将加锁、资源抢夺的业务逻辑交给服务器端处理,减⼩移动客户端的压⼒


@interface ViewController ()
/**
 atomic     是原子属性,是为多线程开发准备的,是默认属性!
            仅仅在属性的 `setter` 方法中,增加了锁(自旋锁),能够保证同一时间,只有一条线程对属性进行`写`操作
            同一时间 单(线程)写多(线程)读的线程处理技术
 nonatomic  是非原子属性
            没有锁!性能高!
 */
@property (nonatomic, copy)   NSString *name;
@end

// 在 OC 中,如果同时重写 了 setter & getter 方法,系统不再提供 _成员变量,需要使用合成指令
// @synthesize name 取个别名:_name
@synthesize name = _name;
#pragma mark - 模拟原子属性示例代码
- (NSString *)name {
    return _name;
}
- (void)setName:(NSString *)name {
    /**
     * 增加一把锁,就能够保证一条线程在同一时间写入!
     */
    @synchronized (self) {
        _name = name;
    }
}

线程间通信

线程间通信常用方法

属性的weak、strong

从xib拖出来的控件是已经被持有了所以用weak
直接创建后直接被清空了

_imageView = [[UIImageView alloc] init];

如果用中间变量持有会维持到作用域结束

UIImageView imageView = [[UIImageView alloc] init];
_imageView = imageView
上一篇 下一篇

猜你喜欢

热点阅读