多线程基础

来源:互联网 发布:阿里算法平台 编辑:程序博客网 时间:2024/06/11 06:12

1.多线程基础

1.1基本概念
1⃣ 进程
进程是指在系统中正在运行的一个应用程序
每个进程之间是独立的,每个进程均运行在其专用且受保护的内存空间内。
2⃣线程
1个进程要想执行任务,必须得有线程(每1个进程至少要有1条线程)线程是进程的基本执行单元,一个进程(程序)的所有任务都在线程中执行。
3⃣多线程
1个进程中可以开启多条线程,每条线程可以并发(同时)执行不同的任务,多线程技术可以提高程序的执行效率。
1.2多线程的原理
同一时间,CPU只能处理1条线程,只有1条线程在工作(执行,多线程并发(同时)执行,其实是CPU快速地在多条线程之间调度(切换)如果CPU调度线程的时间足够快,就造成了多线程并发执行的假象
思考:如果线程非常非常多,会发生什么情况?

CPU会在N多线程之间调度,CPU会累死,消耗大量的CPU资源每条线程被调度执行的频次会降低(线程的执行效率降低)
1.3多线程的优缺点
优点:适当的提高程序的执行效率和资源利用率
缺点:开启线程需要占用一定的内存空间,如果开启大量的线程会占用大量的内存空间,降低程序的性能;线程开启的越多,cpu的地哦啊度线程的开销就越大;开启的线程多,会使程序的设计更加复杂,比如:程序间通信、多线程的数据共享……
1.4多线程在IOS开发中的应用
1.4.1 什么是主线程? 一个iOS程序运行后,默认会开启1条线程,称为“主线程”或“UI线程”

1.4.2 主线程的主要作用

① 显示\刷新UI界面

② 处理UI事件(比如点击事件、滚动事件、拖拽事件等)

*1.4.3主线程的使用注意

别将比较耗时的操作放到主线程中

耗时操作会卡住主线程,严重影响UI的流畅度,给用户一种“卡”的坏体验

1.5 代码
pthread
// MARK:- pthread演练
- (void)pthreadDemo {

/** pthread 是属于 POSIX 多线程开发框架 http://baike.baidu.com POSIX & socket 参数 1. 线程代号的地址 C语言中类型的结尾通常 _t/Ref,而且不需要使用 * 2. 线程的属性 3. 调用函数的指针 4. 传递给该函数的参数 返回值 - 如果是0,表示正确 - 如果是非0,表示错误码 void *(*)(void *) 返回值 (函数指针)(参数) void * 和 OC 中的 id 是等价的 id(*)(id) - 在 ARC 开发中,如果设计到和 C 语言中相同的数据类型进行转换时,需要使用 __bridge “桥接” - 在 MRC 开发中,不需要桥接 在 OC 中,如果是 ARC 开发,编译器会在编译的时候,自动根据代码结构,添加 retain, release, autorelease ARC 只负责 OC 部分的代码,不负责 C 的代码,如果 C 语言的框架出现 retain/create/copy 字样的函数,都需要release 关于桥接的添加,可以利用 Xcode 辅助实现! */// 创建线程pthread_t threadId;NSString *str = @"Hello Pthread";int result = pthread_create(&threadId, NULL, &demo, (__bridge void *)(str));if (result == 0) {    NSLog(@"OK");} else {    NSLog(@"error %d", result);}

}

void *demo(void *param) {
NSString sss = (__bridge NSString )(param);

NSLog(@"%@, %@", [NSThread currentThread], sss);return NULL;

}
NSThread创建方法
// MARK: NSThread 创建
- (void)threadDemo4 {
Person *p = [[Person alloc] init];

[p performSelectorInBackground:@selector(loadData) withObject:nil];

}

  • (void)threadDemo3 {

    // 1
    NSLog(@”1–%@”, [NSThread currentThread]);

    // 是 NSObject 的一个分类方法,意味着所有的 NSObject 都可以使用此方法,在其他线程执行方法!
    // 特点:没有thread字眼,一旦制定,就会立即在后台线程执行 selector 方法
    // performSelectorInBackground 隐式的多线程方法
    // 这种方法,在使用的时候更加灵活!
    [self performSelectorInBackground:@selector(demo:) withObject:@”background”];

    // 1
    NSLog(@”2–%@”, [NSThread currentThread]);
    }

  • (void)threadDemo2 {

    // 1
    NSLog(@”1–%@”, [NSThread currentThread]);

    // detachNewThreadSelector 会理解在后台线程执行 selector 方法
    // detach => 分离一个自线程执行 demo: 方法
    [NSThread detachNewThreadSelector:@selector(demo:) toTarget:self withObject:@”Detach”];

    // 1/2?
    NSLog(@”2–%@”, [NSThread currentThread]);
    }

  • (void)threadDemo1 {

    NSLog(@”1——-“);

    // 实例化/加载 => alloc(分配内存) / init(初始化)
    NSThread *thread = [[NSThread alloc] initWithTarget:self selector:@selector(demo:) object:@”Thread”];

    // 启动线程
    [thread start];

    // 1/2?
    NSLog(@”2——- %@”, [NSThread currentThread]);
    }

  • (void)demo:(id)obj {
    for (int i = 0; i<2; i++) {
    NSLog(@”%@ %@”, [NSThread currentThread], obj);
    }
    }
    线程状态

  • (void)touchesBegan:(NSSet )touches withEvent:(UIEvent )event {
    NSLog(@”走!!!”);
    // 一定注意,不能杀死主线程!
    // [NSThread exit];

    [self threadDemo];
    }

  • (void)threadDemo {
    // 实例化线程对象(新建)
    NSThread *t = [[NSThread alloc] initWithTarget:self selector:@selector(threadStatus) object:nil];
    // 线程就绪(被添加到可调度线程池中)
    [t start];
    }

  • (void)threadStatus {

    // 阻塞,当运行时满足某一个条件,会让线程”休眠”/锁
    // 提示:sleep 方法是类方法,会直接休眠当前线程
    NSLog(@”睡会”);
    // 直接睡两秒
    [NSThread sleepForTimeInterval:2.0];

    for (int i = 0; i < 20; i++) {
    if (i == 8) {
    // 线程执行中,满足某一个条件时,再次休眠
    NSLog(@”再睡会”);
    // 睡到指定的日期,从现在开始过了多久
    [NSThread sleepUntilDate:[NSDate dateWithTimeIntervalSinceNow:2.0]];
    }

    NSLog(@"%@ %d", [NSThread currentThread], i);// 当线程满足某一个条件时,可以强行终止if (i == 15) {    // 一旦强行终止线程,后续的所有代码都不会被执行    // 注意:在终止线程之前,应该注意释放之前分配的对象!    // 如果是 ARC 开发,需要注意,清理 C 语言框架创建的对象!否则会出现内存泄漏!    [NSThread exit];}

    }
    NSLog(@”能来吗?”);
    }
    NSThread属性
    /**
    优先级只是保证 CPU 调度的可能性会高!

    多线程的目的:将耗时的操作放在后台,不阻塞主线程和用户的交互!
    个人建议,在开发的时候,不要修改优先级!优先级翻转!
    多线程开发,有一个原则:尽量简单!

    在多线程开发中,不要相信一次运行的结果!
    */

  • (void)threadDemo {
    NSThread *t1 = [[NSThread alloc] initWithTarget:self selector:@selector(demo) object:nil];

    // 在大的商业项目中,通常希望程序崩溃的时候,能够获取到程序准确执行所在的线程!
    t1.name = @”Thread A”;

    // 优先级,是一个浮点数,从0~1.0,1.0表示优先级最高,默认优先级是0.5
    // 优先级只是保证 CPU 调度的可能性会高!
    t1.threadPriority = 0.1;
    [t1 start];

    //———————-

    NSThread *t2 = [[NSThread alloc] initWithTarget:self selector:@selector(demo) object:nil];

    // 在大的商业项目中,通常希望程序崩溃的时候,能够获取到程序准确执行所在的线程!
    t2.name = @”Thread B”;

    // 优先级,是一个浮点数,从0~1.0,1.0表示优先级最高,默认优先级是0.5
    t2.threadPriority = 1;
    [t2 start];
    }

  • (void)demo {
    for (int i = 0; i < 10; i++) {
    NSLog(@”%@ %d”, [NSThread currentThread], i);
    }

// // 判断是否是主线程
// if (![NSThread isMainThread]) {
// // 模拟崩溃
// NSMutableArray *array = [NSMutableArray array];
// [array addObject:nil];
// }
}
nonatomic与 atomic
nonatomic: 非原子属性
atomic: 原子属性(线程安全),就是针对多线程设计的,是默认属性
多个线程写入属性时,保证同一时间只有一个线程能够执行写入操作
单(线程)写多(线程)读的一种多线程技术,同样有可能出现“脏数据”,重新读一下。

实际上,原子属性内部也有一把锁,自旋锁

自旋锁 & 互斥锁

-共同点
都能够保证同一时间,只有一条线程执行锁定范围的代码
-不同点
互斥锁:如果发现有其他线程正在执行锁定的代码,线程会进入休眠状态,等待其他线程执行完毕,打开锁之后,线程会被唤醒
自旋锁:如果发现有其他线程正在执行锁定的代码,线程会用死循环的方式,一直等待锁定代码执行完成!
自旋锁更适合执行非常短的代码!

  • 无论什么锁,都是要付出代价的!

  • 线程安全

    在多个线程进行读写操作时,仍然能够保证数据正确!

  • UI 线程,共同约定:所有更新 UI 的操作都在主线程上执行!
    原因:几乎所有的 UIKit 都不是线程安全的!”取舍”

    日常开发中,使用锁的机会很少,多线程的目的,就是将耗时的操作放在后台!
    线程间通讯
    在主线程更新 UI
    /**
    performSelectorOnMainThread “线程间通讯”

    1. 在主线程执行的方法
    2. 传递给方法的参数
    3. 是否等待被调用方法执行完成,有可能也会等待调用方法的执行完成!几率极少!
      */
      [self performSelectorOnMainThread:@selector(setImage:) withObject:image waitUntilDone:NO];
      定时器
      • (void)viewDidLoad {
        [super viewDidLoad];

    /**
    NSDefaultRunLoopMode - 时钟,网络事件;
    NSRunLoopCommonModes - 用户交互;
    */
    // 以下两句两种添加时钟的方法是等价的
    NSTimer *timer = [NSTimer timerWithTimeInterval:1.0 target:self selector:@selector(updateTimer) userInfo:nil repeats:YES];
    // // 加入运行循环
    [[NSRunLoop currentRunLoop] addTimer:timer forMode:NSRunLoopCommonModes];
    // scheduledTimerWithTimeInterval 默认就是使用 NSDefaultRunLoopMode
    // [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:@selector(updateTimer) userInfo:nil repeats:YES];
    }

    • (void)updateTimer {
      static int num = 0;

    // 如果时钟触发方法,执行非常耗时的操作!注意不能在时钟调用方法中,执行耗时的操作!
    NSLog(@”睡会”);
    [NSThread sleepForTimeInterval:1.0];

    NSLog(@”%@ %d”, [NSThread currentThread], num++);
    }

0 0
原创粉丝点击