多线程

-----------进程和线程------------

  • 1:进程
  • 1.1:系统中正在运行的程序,称为一个进行
  • 1.2:每个进程,都在自己独立的内存空间运行,并且进程之间互不影响
  • 2:线程
    • 2.1:进程的所有任务,都是在线程执行的,可以说它是进程的基本执行单元
    • 2.2:进程的串行.
      • 2.2.1:单个进程中执行的任务是串行的,每个任务都是按顺序执行的
        
      • 2.2.2:单个线程,同一时间内,只能执行一个任务
        
  • 3:进程和线程的比较
    • 3.1:进程:
      • 3.1.1:进程是CPU分配资源和调度的单位
        
      • 3.1.2:同一个进程中的线程,共享该进程下的资源
        
    • 3.2:线程
      • 3.2.1:线程是CPU调用(执行任务)的最小单位
        
      • 3.2.2:一个进程可以有多个线程
        

--------------多线程--------------

  • 1:概念:一个进程可以由多个线程,每个线程可以并发执行不同的任务(eg: 下载一个软件,可以使用3个线程同时下载不同的文件)
  • 2:原理:
    • 2.1:同一时间,CPU只能调用一条线程,也就是说,同一时间只有一条线程在工作(时间极短)
    • 2.2:并行,就是CPU(特指单核)以最快的速度,在不同线程之间进行切换,调度,从而造成了并行的假象
    • 2.3:多核CPU,每个核都可以同时处理不同任务,从而真正达到了多线程并发执行任务
  • 3:多线程优缺点:
    • 3.1:优点:
      • 3.1.1:提高程序运行效率
      • 3.1.2:提高了资源利用率,充分使用了空间的内存和CPU资源
    • 3.2:缺点:
      • 3.2.1:每创建一条线程,都会开辟新的内存空间,并且消耗大约90毫秒的时间
      • 3.2.2:如果创建过多的线程,势必会拖慢CPU的运行速度,降低程序性能,让CPU开销过大
      • 3.2.3:多线程设计上往往有些困难,线程间的切换,主线程子线程的协调,都是开发中较难解决的问题

------多线程在iOS开发中的应用-------

  • 1:主线程
- 1.1:在应用程序启动之后,在UIApplication中会自动开启一条主线程(UI线程)
- 1.2:主线程的作用,主要是用来显示刷新UI界面,处理UI事件
  • 2,使用时的注意事项
    • 2.1:耗时的操作(加载时间过长的操作),不要放在主线程里,否则会影响主线程处理UI事件,导致运行界面拖慢,降低用户体验
    • 2.2:当耗时操作还没有结束时,UI界面无法响应用户的交互,直到耗时操作结束后,主线程才能继续处理UI事件
    • 2.3:因此:耗时操作,应该放在子线程中执行(例如后台线程)

iOS多线程实现种类###

NSPerformSelector
** NSThread**:优点:轻量级的多线程. 缺点:需要自己管理线程的生命周期,线程同步

- (void)viewDidLoad {
    [super viewDidLoad];

       //开辟子线程的方法
#pragma mark-----1,使用NSPerformSelector开辟子线程(在后台执行某个方法)-----
    /*
     performSelectorInBackground:@selector(SayHi:) withObject:@"dfklr"
     参数1:需要使用子线程执行的方法
     参数2:传递的参数
     */
    [self performSelectorInBackground:@selector(SayHi:) withObject:@"dfklr"];

    
#pragma mark----2.使用NSThread手动开辟子线程------
//创建线程(NSThread)
    NSThread *thread = [[NSThread alloc]initWithTarget:self selector:@selector(SayHi:) object:nil];
    //开启线程
   [thread start];
    //关闭线程(可写可不写);
    [NSThread exit];
//    //取消线程(实际上就是做了个标记,表示被取消)
    [thread cancel];
    
#pragma mark----3.使用NSThread自动开辟子线程(无需手动)-----
    //延时方法
    [NSThread sleepForTimeInterval:3];
    [NSThread detachNewThreadSelector:@selector(SayHi:) toTarget:self withObject:@"i++"];
    
 
}



- (void)SayHi:(NSString *)str
    //为保证对象及时释放,在手动创建多线程方法中需要添加自动释放池
    @autoreleasepool {
   NSLog(@"------------------嗨,girl%@",str);
    NSLog(@"mianThread = %@",[NSThread mainThread]);
    NSLog(@"currentThread= %@",[NSThread currentThread]);
  //使用NSObject回到主线程
    /*
     performSelectorOnMainThread:<#(nonnull SEL)#> withObject:<#(nullable id)#> waitUntilDone:<#(BOOL)#>
     参数1:回到主线程之后需要做的事情
     参数2:传递的参数
     参数3:判断方法是否执行完毕
     YES:先执行方法,再回到该函数,执行该函数后面的函数
     */
    NSLog(@"before");
    [self performSelectorOnMainThread:@selector(onMainThread) withObject:nil waitUntilDone:YES];
    NSLog(@"after");
    }
}

- (void)onMainThread{
    NSLog(@"callBack");
    NSLog(@"%@",[NSThread currentThread]);
    NSLog(@"%d",[NSThread isMainThread]);
}


NSOperationQueue和NSOperation

  • 1.NSOperation
  • 1.1:首先它是一个抽象类,所以执行任务的是它的子类:NSInvocationOperation和NSBlockOperation,这两个子类,相当于一个方法选择器"prefromSelector()",由它俩本身发起的任务,并不是在子线程中执行
  • 1.2:NSOperation和它子类,本身并不会进行线程的创建,所以,在他们的任务方法中打印当前线程,显示的为主线程
  • 1.3:NSOperation和它的子类,只是一个操作,和多线程没有关系,本身没有主线程,子线程之分,可以在任何线程中使用,通常和NSOperationQueue结合使用
  • 2:NSOperationQueue
    一个NSOperationQueue操作队列,就相当于一个线程管理器,将NSOperation和子类的对象放入队列中,然后由队列负责派发任务,所以NSOperationQueue并不是一个线程,但是,你可以设置队列中运行的线程的数量
    优点:
    不需要手动关联线程,只需要把精力放在自己要执行的操作上面
    缺点:
    它是基于OC对象的,那么相对于基于C函数来说,效率要低于GCD,GCD提供的功能比他更全面
- (void)viewDidLoad {
    [super viewDidLoad];
    //子类1:NSInvocationOperation
    NSInvocationOperation *operation = [[NSInvocationOperation alloc]initWithTarget:self selector:@selector(haha) object:nil];
    //operation在单独使用的时候需要手动调用开启方法
//    [operation start];

//子类2:NSBlockOperation
    NSBlockOperation *blockOperation = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"block:%@",[NSThread currentThread]);
        NSLog(@"block:%@",[NSThread mainThread]);
        NSLog(@"block:%d",[NSThread isMainThread]);

    }];
//开启
 //   [blockOperation start];
//    NSOperationQueue创建多线程
//    如果搭配了NSOperationQueue中的add方法创建多线程的话,就不需要使用Start方法,否则会崩溃.
//    创建队列
    NSOperationQueue *queue= [[NSOperationQueue alloc]init];
 //设置最大并发数
    /*
当设置最大并发数为1时,执行顺序也为串行
    当设置最大并发数大于1时,叫并行,多条通道同时进行各自的任务,互不影响
*/
    queue.maxConcurrentOperationCount = 1;
    //给队列添加对象
    [queue addOperation:operation];
    [queue addOperation:blockOperation];
}

- (void)haha{
    NSLog(@"haha:%@",[NSThread currentThread]);
    NSLog(@"haha:%@",[NSThread mainThread]);
    NSLog(@"haha:%d",[NSThread isMainThread]);

}

GCD

  • 1:特点:
  • 1.1:纯C语言编写,所以在使用的时候,使用的是函数而不是方法
  • 1.2:GCD可以充分利用多核硬件并发处理多个任务,提高效率
  • 1.3:GCD使用后,不用程序去管理线程的开闭,GCD会在系统层上面动态的检测系统状态,开闭线程
  • 1.4:管理线程的生命周期(调度任务,销毁线程,创建线程)
  • 2:核心概念:
  • 2.1:任务:执行什么操作
  • 2.2:队列:用来存放任务
  • 3.使用GCD的两个步骤
  • 3.1:定制任务:确定需要做什么事情
  • 3.2:将任务添加到队列中
    • 3.2.1:GCD会自动将队列中的任务取出,放到对应的线程中
    • 3.2.2:而任务的取出,遵循队列的先进先出(FIFO)原则
  • 4.队列
  • 4.1:并发队列
    • 4.1.1:可以让多个任务并发(同时)执行(自动开启了多个线程,同时执行任务)
    • 4.1.2并发功能只能在异步函数下才会有效
  • 4.2:串行队列:让任务一个接一个执行(一个完成,执行下一个)
  • 4.3:并发和串行,决定了任务的执行方式
    • 4.3.1并发:多个任务同时执行
    • 4.3.2串行:多个任务挨个执行
#pragma mark----队列-------
- (void)queue{
//创建串行队列
    dispatch_queue_t queue2 = dispatch_queue_create("queue2", 0);
    //将任务添加到队列当中
    //串行队列+同步任务
    /*
    dispatch_sync(dispatch_get_main_queue(), ^{
        NSLog(@"串行加同步%@",[NSThread currentThread]);
    });
     线程锁死:
     原因: 1:dispatch_sync在等待任务执行完成,但是任务又被添加到主线程里,在主线程里执行,所以 dispatch_sync如果在主线程调用,就会造成锁死
    2:dispatch_sync是同步的,本身就会阻塞线程,就是主线程,而现在又往主线程添加任务,就会被发送锁死.如上
        */
    dispatch_sync(queue2, ^{
        NSLog(@"串行加同步%@",[NSThread currentThread]);
    });

#pragma mark---创建并发队列的两种方式-----
    
    //第一种:系统方法创建并发队列(全局队列)
    /*
     dispatch_get_global_queue(<#long identifier#>, <#unsigned long flags#>)
     参数1:优先级(有4个)
     参数2:系统保留关键字,暂时写0
     */
    dispatch_queue_t queues = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0);
    //第二种:
    dispatch_queue_t queuem = dispatch_queue_create("queuem", DISPATCH_QUEUE_CONCURRENT);
    //并发+同步任务
    dispatch_sync(queues, ^{
        NSLog(@"并发+同步%@",[NSThread currentThread]);
    });
    //并发+异步任务(可以开启子线程)
    dispatch_async(queuem, ^{
        NSLog(@"并发+异步%@",[NSThread currentThread]);
    });
    
}

#pragma mark----Sleep------
/*
 dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
 NSLog(<#NSString * _Nonnull format, ...#>)
 });
参数1:计算时间(从现在开始计时,(int64_t)(真正延迟的时间 *NSEC_PER_SEC))
 参数2:任务
 */
- (void)sleep{
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
    NSLog(@"已经3秒");
});

}

#pragma mark----向队列中重复添加任务---------
- (void)reAdd{
//创建队列
    dispatch_queue_t queue = dispatch_queue_create(0, DISPATCH_QUEUE_CONCURRENT);
    
    /*
     dispatch_apply(<#size_t iterations#>, <#dispatch_queue_t queue#>, <#^(size_t)block#>)
     参数1:添加的次数
     参数2:队列
     参数3:任务
     */
    dispatch_apply(10, queue, ^(size_t index) {
        NSLog(@"index_%zu",index);
    });
}

#pragma mark----分组-----
- (void)group{
//创建分组
    dispatch_group_t group = dispatch_group_create();
    //创建一个并发队列
    dispatch_queue_t queue = dispatch_queue_create("queue", DISPATCH_QUEUE_CONCURRENT);
    //先分组添加任务
    dispatch_group_async(group, queue, ^{
        NSLog(@"我是1");
    });
    //用来监听组内任务,当组内任务全部执行完毕后,才执行此函数(组内必须现有任务,才可监听)
    dispatch_group_notify(group, queue, ^{
        NSLog(@"我是最后一个");
    });
    dispatch_group_async(group, queue, ^{
        NSLog(@"我是2");
    });
    dispatch_group_async(group, queue, ^{
        NSLog(@"我是3");
    });

}

#pragma mark---并发中的串行-----
- (void)heid{
//创建串行队列
    dispatch_queue_t queue = dispatch_queue_create("queue", 0);
    //在串行队列中创建异步任务(开辟新线程,执行任务)
    dispatch_async(queue, ^{
        NSLog(@"我是查询1%@",[NSThread currentThread]);

    });
    dispatch_async(queue, ^{
         NSLog(@"我是查询2%@",[NSThread currentThread]);
    });
    dispatch_async(queue, ^{
         NSLog(@"我是查询3%@",[NSThread currentThread]);
    });
    dispatch_async(queue, ^{
         NSLog(@"我是查询4%@",[NSThread currentThread]);
    });
}


#pragma mark---应用----
- (void)GCDuser{
    NSURLSession *session = [NSURLSession sharedSession];
    NSURLSessionTask *task = [session dataTaskWithURL:[NSURL URLWithString:@"https://www.baidu.com"] completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
        if (!error) {
            //解析
            //回到主线程刷新表格
            dispatch_async(dispatch_get_main_queue(), ^{
                //刷新UI
                //[self.tableView reloadData];
            });
        }
    }];
    
    [task resume];
}

此外补充一点单例上的线程问题

static MyHandel *myhandle = nil ;
//如果在两个并发进程中同时走这个初始化方法时,就会被初始化两次,也就违背了单例的特征(整个应用中,只初始化一次)
+(MyHandel *)sharehandle{
    //不仅仅意味着代码只运行一次,而且还是线程安全的
    static dispatch_once_t once;
    //表示同一时间内,只有一个线程,可以访问block里的内容
    dispatch_once(&once, ^{
        myhandle = [[MyHandel alloc]init];
    });
    return myhandle;
}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,590评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 86,808评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,151评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,779评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,773评论 5 367
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,656评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,022评论 3 398
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,678评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 41,038评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,659评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,756评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,411评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,005评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,973评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,203评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,053评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,495评论 2 343

推荐阅读更多精彩内容