多线程

多线程(四种实现方式)
1.NSThread
2.NSObject
3.NSOperation/NSOperationQueue
4.GCD
进程:一个独立运行的程序可以看做是一个进程。
线程:进程中的独立代码段。
通常一个程序只有一个进程,一个进程有1-n个线程(最少有一个主线程运行程序)
只有主线程的程序叫单线程程序,有主线程和子线程的程序称为到线程程序

<h6>应用程序会默认为主线程分配1M的栈空间,默认为子线程分为512K的栈空间(分配必须是4K的整数倍)。主线程和子线程的栈区是相互独立的。</h6>

<h6>主线程从main函数开始,所有的内容全部在自动释放池管辖范围内。子线程新开辟的区域在处理任务时,并没有在自动释放池的管辖范围内,若在堆区开辟空间而不进行内存处理,会造成大量的泄漏(比如短时间内循环使用便利构造器等),所以人为开辟子线程必须要添加@autoreleasepool来管辖堆区的内容。主线程和子线程的堆区是共有的。

<1>NSThread

1.1使用示例对象来开辟子线程(手动开启)

// 参数:方法的执行者、子线程需要执行的方法、传递的参数
NSThread *thread =[[NSThread alloc]initWithTarget:self selector:@selector(calculateAction) object:nil];
[thread start];// 必须手动开动才会执行子线程内容

// 取消子线程
// [thread cancel];
// 查看当前线程是否正在执行
NSLog(@"在执行%d",[thread isExecuting]);
// 查看当前线程是否取消
NSLog(@"已取消%d",[thread isCancelled]);



// 打印当前方法是否为主线程
NSLog(@"是否为主线程%d",[NSThread isMainThread]);
// 打印当前线程
NSLog(@"当前线程%@",[NSThread currentThread]);

1.2使用静态方法来开辟子线程(自动开启)

[NSThread detachNewThreadSelector:@selector(calculateAction) toTarget:self withObject:nil];

<2>NSObject

[self performSelectorInBackground:@selector(calculateAction) withObject:nil];
NSLog(@"主线程中开辟子线程代码.....");

<3>NSOperationQueue:

此种多线程方式和NSOperation的两个子类来结合使用,实现多线程方式
// 3.1
NSInvocationOperation *invocation = [[NSInvocationOperation alloc]initWithTarget:self selector:@selector(calculateAction) object:nil];
// 3.2
NSBlockOperation *block = [NSBlockOperation blockOperationWithBlock:^{
    
    [self calculateAction];
}];
// 创建管理队列的两个任务
NSOperationQueue *queue = [NSOperationQueue new];

// 设置最大并发数(同时执行任务的个数)
// 若最大并发数设置为1,那么任务将按照串行方式来执行
[queue setMaxConcurrentOperationCount:1];
// 将任务添加到队列
[queue addOperation:invocation];
[queue addOperation:block];

<4>GCD

// 两种队列方式:并行队列,串行队列
// 并行队列:所有任务并发执行,不分先后
// 串行队列:所有任务依次执行,排队完成

// GCD中,有三种队列可以使用
// 主队列:系统提供的单列,属于串队列
// 全局队列:系统提供的单列,属于并行队列
// 自定义队列:开发人员可以自定义选择使用串行或者并行

4.1主队列(int a = 10;主队列的任务在主线程中执行)

dispatch_queue_t mainQueue = dispatch_get_main_queue();

// 给队列添加任务
dispatch_async(mainQueue, ^{
    
    NSLog(@"第一个任务%d",[NSThread isMainThread]);
});

dispatch_async(mainQueue, ^{
        
    NSLog(@"第二个任务%d",[NSThread isMainThread]);
});

dispatch_async(mainQueue, ^{
    
    NSLog(@"第三个任务%d",[NSThread isMainThread]);
});

4.2全局队列:并行队列,会开辟子线程,但是其管理的任务不一定只在子线程执行,也会添加到主线程执行

// 1.队列优先级
// 2.空余参数,以后添加作用   
dispatch_queue_t globalQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);

// 必须先执行我(第一个加入队列时,才会优先执行此任务(在主线程中执行),否则和其他三个任务一样无法确认哪个先执行)
dispatch_barrier_sync(globalQueue, ^{// 此处是_sync不是_async
    
    NSLog(@"优先执行的代码......%d",[NSThread isMainThread]);
});

// 向队列中添加任务(三个任务在子线程中同步执行,打印结果没有固定的先后顺序)
dispatch_async(globalQueue, ^{
    
    NSLog(@"第一个任务%d",[NSThread isMainThread]);
});

dispatch_async(globalQueue, ^{
    
    NSLog(@"第二个任务%d",[NSThread isMainThread]);
});

dispatch_async(globalQueue, ^{
    
    NSLog(@"第三个任务%d",[NSThread isMainThread]);
});
延时启动任务
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(10 * NSEC_PER_SEC)), globalQueue, ^{
    
    NSLog(@"出来吧,神龙!");
});
反复执行n次的任务
dispatch_apply(3, globalQueue, ^(size_t time) {
    
    NSLog(@"第%zu次",time);
});
函数
void function(void * string);
void function(void * string){

    NSLog( @"%s",string);
}
添加此函数到队列
// 1.要把任务添加到的队列
// 2.函数的参数
// 3.函数
dispatch_async_f(globalQueue, "fall in love with", function);

4.3自定义队列

串行队列

// 1.给队列一个标签,后续可以获取队列的标签来操作
dispatch_queue_t serialQueue = dispatch_queue_create("com.lanou3g.mySerialQueue", DISPATCH_QUEUE_SERIAL);
// async和sync添加任务的区别在于是否同时执行block和外部的代码(即子线程和主线程是否同时执行),前者会同时执行,后者则是顺序执行
dispatch_async(serialQueue, ^{
   
    NSLog(@"第一个任务%d",[NSThread isMainThread]);
});
NSLog(@"1");

dispatch_async(serialQueue, ^{
    
    NSLog(@"第二个任务%d",[NSThread isMainThread]);
});
NSLog(@"2");

dispatch_async(serialQueue, ^{
    
    NSLog(@"第三个任务%d",[NSThread isMainThread]);
});
NSLog(@"3");

并行队列

dispatch_queue_t concurrentQueue = dispatch_queue_create("myConcurrentQueue", DISPATCH_QUEUE_CONCURRENT);

/*
dispatch_async(concurrentQueue, ^{
    
    NSLog(@"第一个任务%d",[NSThread isMainThread]);
});

dispatch_async(concurrentQueue, ^{
    
    NSLog(@"第二个任务%d",[NSThread isMainThread]);
});

dispatch_async(concurrentQueue, ^{
    
    NSLog(@"第三个任务%d",[NSThread isMainThread]);
});
*/
/*
// 分组:使用一个组来操作一组任务
dispatch_group_t group = dispatch_group_create();

dispatch_group_async(group, concurrentQueue, ^{
   
    NSLog(@"第一个分组任务%d",[NSThread isMainThread]);
});

dispatch_group_async(group, concurrentQueue, ^{
    
    NSLog(@"第二个分组任务%d",[NSThread isMainThread]);
});

dispatch_group_async(group, concurrentQueue, ^{
    
    NSLog(@"第三个分组任务%d",[NSThread isMainThread]);
});

// 等待分组中所有任务执行完毕后再执行此任务(必须写在后面)
dispatch_group_notify(group, concurrentQueue, ^{
    
    NSLog(@"撩汉终结者,kingStar");
});

*/

子线程回到主线程的演示

dispatch_queue_t globalQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
// 添加子任务
dispatch_async(globalQueue, ^{
   
    [self calculateAction];
});

// 主、全局无论哪种队列都遵循FIFO(先进先出)
用来耗时的计算(多线程测试的使用)
- (void)calculateAction{
    // 让子线程睡10秒再干活
    [NSThread sleepForTimeInterval:2];

    // 当子线程中出现对象类型时,需要使用自动释放池包裹对应的代码
    @autoreleasepool {
    
        int sum = 0;
        for (int i = 0; i < 655350000; i++ ){
            sum += i;
        }
        NSLog(@"%d",sum);

        // 子线程回到主线程的第一种方式
        // 1.执行在主线程的方法
        // 2.传递参数
        // 3.是否等到完成后操作
         NSNumber *nsnum = [NSNumber numberWithInt:sum ];
         [self performSelectorOnMainThread:@selector(mainAction:) withObject:nsnum waitUntilDone:YES];
        // 打印当前方法是否为主线程
        NSLog(@"===%d",[NSThread isMainThread]);
        // 打印当前线程
        NSLog(@"===%@",[NSThread currentThread]);
         
        // 子线程回到主线程的第二种方式
        dispatch_async(dispatch_get_main_queue(), ^{
            NSLog(@"%d",sum);
            NSLog(@"%d",[NSThread isMainThread]);
        });
    }
}

// 子线程回到主线程的第一种方式调用的mainAction:方法
- (void)mainAction:(NSNumber *)sum{

    NSLog(@"计算的结果为%d",[sum intValue]);
    NSLog(@"当前线程是否为主线程%d",[NSThread isMainThread]);
}

线程按照生命周期分为两种
脱离线程:线程使用完毕后,即被销毁,不可在唤醒
非脱离线程:线程任务执行时,始终处于被唤醒的状态,一旦收集到任务,马上启动,生命周期长,可被唤醒。

// 多线程操作(两队人购票)

// 初始化开辟空间
self.lock = [NSLock new];

// 多线程容易出现线程互斥问题

dispatch_queue_t globalQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);

dispatch_async(globalQueue, ^{
    
   // 第一队人在购票
    for (int i = 0; i < 1000; i++) {
        [self buyTicket:@"王冲"];
    }
});

dispatch_async(globalQueue, ^{
    
    // 第二队人在购票
    for (int i = 0; i < 1000; i++) {
        [self buyTicket:@"JonKing"];
    }
});

// atomic和nonatomic实现
// atomic是线程安全的,使用@synchronized进行对对象的加锁操作,但是只能是对象类型。
// nonatomic是线程不安全的,没有对数据做任何处理。
// 举例代码:下列代码的意思:当访问到self对象时,写在大括号内部的代码会被保证同一时间只允许一个任务在访问。
@synchronized(self) {
    // 操作
}
购票方法
int count = 5000;
- (void)buyTicket:(NSString *)str{

    // 每次遭遇访问时,要进行加锁操作
    [self.lock lock];


    count -- ;// 模拟每次买票造成的结果,票数减一

    NSLog(@"%@购的一张票,剩余%d张票",str,count);


    // 资源访问完毕时,进行解锁让下一个任务访问
    [self.lock unlock];

}

其他资料 作者 YotrolZ 多线程的实现方案(4种)

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

推荐阅读更多精彩内容

  • #import "ViewController.h" @interface ViewController () @...
    艾克12138阅读 252评论 0 0
  • #import "ViewController.h" @interface ViewController () @...
    艾克12138阅读 211评论 0 0
  • iOS 多线程系列 -- 基础概述iOS 多线程系列 -- pthreadiOS 多线程系列 -- NSThrea...
    shannoon阅读 832评论 0 2
  • 一、前言 本篇博文介绍的是iOS中常用的几个多线程技术: NSThread GCD NSOperation 由于a...
    和珏猫阅读 572评论 0 1
  • 无名的卑微的崇拜者呀 为何你要把那耻辱愉快的信仰 不羁的狂放的邪教徒呀 你的快乐的毒酒 曾毒害了一个纯洁而高傲的心...
    答案在风中飞扬阅读 342评论 0 1