多线程

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

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

主线程从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种)

你可能感兴趣的:(多线程)