本文介绍: 进程与线程的关系进程是指在系统中正在运行的一个应用程序。每个进程之间是独立的,每个进程均运行在其专用且受保护的内存空间内1个进程要想执行任务,必须得有线程(每1个进程至少要有1条线程,称为主线程)。一个进程(程序)的所有任务都在线程中执行,同一时间,CPU只能处理1条线程,只有1条线程在工作(执行),多线程并发(同时)执行,其实是CPU快速地在多条线程之间调度(切换),如果CPU调度线程的时间足够快,就造成了多线程并发执行的假象。NSThreadNSThread是OC线程的基础类,可以通过sta

进程与线程的关系

进程是指在系统中正在运行的一个应用程序。每个进程之间是独立的,每个进程均运行在其专用且受保护的内存空间内1个进程要想执行任务,必须得有线程(每1个进程至少要有1条线程,称为主线程)。一个进程(程序)的所有任务都在线程中执行,
同一时间,CPU只能处理1条线程,只有1条线程在工作(执行),多线程并发(同时)执行,其实是CPU快速地在多条线程之间调度(切换),如果CPU调度线程的时间足够快,就造成了多线程并发执行的假象。

NSThread

NSThread是OC线程的基础类,可以通过start sleep cancel控制线程执行情况。

// 方法一:创建线程,需要自己开启线程(可控制)
    NSThread *thread = [[NSThread alloc]initWithTarget:self selector:@selector(run) object:nil];
    // 开启线程
    [thread start];
    
    // 方法二:创建线程后自动启动线程
    [NSThread detachNewThreadSelector:@selector(run) toTarget:self withObject:nil];
    // 方法三:隐式创建并启动线程
    [self performSelectorInBackground:@selector(run) withObject:nil];

GCD

GCD会自动利用更多的CPU内核
GCD自动管理线程的生命周期(创建线程,调度任务,销毁线程等)

GCD中有2个核心概念:任务和队列
任务
同步:只能在当前线程中执行任务,不具备开启新线程的能力,任务立刻马上执行,会阻塞当前线程并等待 Block中的任务执行完毕,然后当前线程才会继续往下运行
异步:可以在新的线程中执行任务,具备开启新线程的能力,但不一定会开新线程,当前线程会直接往下执行,不会阻塞当前线程

队列
判定一些操作往一个队列中添加时如何执行,先看是往串行还是并行队列中加任务,如是串行必然是顺序执行的,如果是并行异步才会交叉执行。
//全局并发队列
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
// 串行队列
dispatch_queue_t queue = dispatch_queue_create("test", DISPATCH_QUEUE_SERIAL);
// 并发队列
dispatch_queue_t queue1 = dispatch_queue_create("test", DISPATCH_QUEUE_CONCURRENT);

栅栏函数可以控制任务执行的顺序,栅栏函数之前的执行完毕之后,执行栅栏函数,然后在执行栅栏函数之后的(并发队列)
dispatch_barrier_async(queue, ^{ });

队列组
当有多个异步任务的时候,组内全部执行完毕再执行dispatch_group_notify可以起到监听作用,控制业务流程。

 
    // 创建队列组
    dispatch_group_t group = dispatch_group_create();
    // 创建并行队列
    dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
    // 执行队列组任务
    dispatch_group_async(group, queue, ^{
        NSLog(@"异步任务1");
    });
    // 执行队列组任务
    dispatch_group_async(group, queue, ^{
        NSLog(@"异步任务2");
    });
    //队列组中的任务执行完毕之后,执行该函数
    dispatch_group_notify(group, queue, ^{
        NSLog(@"组内执行完毕任务,此处执行");
    });

信号量

// 创建信号量,并且设置值为0
    dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    
    dispatch_async(queue, ^{
        //发送一个信号,自然会让信号总量加1,
        dispatch_semaphore_signal(semaphore);
    });
    //阻塞主线程
    //当信号总量少于0的时候就会一直等待,否则就可以正常的执行,并让信号总量-1
    dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
    NSLog(@"等待信号量大于0才会执行”);

//利用信号量进行并发控制(5次)
    dispatch_semaphore_t sema = dispatch_semaphore_create(5);
    for (100次循环操作) {
        dispatch_semaphore_wait(sema, DISPATCH_TIME_FOREVER);
        dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
            // 操作
            dispatch_semaphore_signal(sema);
        });
    }

dispatch_group_enter和dispatch_group_leave
利用dispatch_group_enter和dispatch_group_leave控制线程,因为二者必须一一对应,只有leave之后,才能进行下一个enter。如:利用这两个函数实现AFNetWorking多个请求依赖的线程同步问题,这样就能让下一个异步请求在上一个执行完毕后再执行。而不需要写在回调内。

dispatch_group_t group = dispatch_group_create();
    dispatch_group_enter(group);
    AFNetWorking{
        dispatch_group_leave(group);
        NSLog(@"异步任务1");
    }
    dispatch_group_enter(group);
    AFNetWorking{
        dispatch_group_leave(group);
        NSLog(@“异步任务2");
    }   
    dispatch_group_notify(group, queue, ^{
        NSLog(@"组内执行完毕任务,此处执行");
    });

NSOperationQueue

NSOperation 是苹果公司对 GCD 的封装,完全面向对象,并比GCD多了一些更简单实用的功能,所以使用起来更加方便易于理解,主要优势有设置依赖,优先级设置,可继承,键值对观察。NSOperation 和NSOperationQueue 分别对应 GCD 的 任务 和 队列。
NSOperation和NSOperationQueue实现多线程的具体步骤
1.将需要执行的操作封装到一个NSOperation对象中
2.将NSOperation对象添加到NSOperationQueue中
系统会自动将NSOperationQueue中的NSOperation取出来,并将取出的NSOperation封装的操作放到一条新线程中执行
如果将NSOperation添加到NSOperationQueue(操作队列)中,系统会自动异步执行NSOperation中的操作
3.最大并发数:当maxConcurrentOperationCount为1时,则表示不开线程,也就是串行,默认为-1,大于1都为并行。
4.NSOperationQueue可以cancel,suspend(暂停)。暂停和取消不是立刻取消当前操作,而是等当前的操作执行完之后不再进行新的操作

操作依赖

// 创建非主队列
    NSOperationQueue *queue = [[NSOperationQueue alloc]init];
    // 下载第一张图片
    NSBlockOperation *download1 = [NSBlockOperation blockOperationWithBlock:^{
    }];
    // 下载第二张图片
    NSBlockOperation *download2 = [NSBlockOperation blockOperationWithBlock:^{
    }];
    // 合成操作
    NSBlockOperation *combie = [NSBlockOperation blockOperationWithBlock:^{
        // 回到主线程刷新UI
        [[NSOperationQueue mainQueue]addOperationWithBlock:^{
            //合成图片
        }];
    }];
    // 添加依赖,合成图片需要等图片1,图片2都下载完毕之后合成
    [combie addDependency:download1];
    [combie addDependency:download2];
    // 添加操作到队列
    [queue addOperation:download1];
    [queue addOperation:download2];
    [queue addOperation:combie];

读写锁

self.concurrentQueue = dispatch_queue_create("aaa", DISPATCH_QUEUE_CONCURRENT);
// 写操作,栅栏函数是不允许并发的,所以"写操作"是单线程进入的,根据log可以看出来
- (void)setText:(NSString *)text {
    
    __weak typeof(self) weakSelf = self;
    dispatch_barrier_sync(self.concurrentQueue, ^{
        __strong typeof(weakSelf) strongSelf = weakSelf;
        strongSelf->_text = text;
        NSLog(@"写操作 %@ %@",text,[NSThread currentThread]);
        // 模拟耗时操作,1个1个执行,没有并发
        sleep(1);
    });
}
// 读操作,这个是可以并发的,log在很快时间打印完
- (NSString *)text {
 
    __block NSString * t = nil ;
    __weak typeof(self) weakSelf = self;
    dispatch_sync(self.concurrentQueue, ^{
        __strong typeof(weakSelf) strongSelf = weakSelf;
        t = strongSelf->_text;
        // 模拟耗时操作,瞬间执行完,说明是多个线程并发的进入的
        sleep(1);
 
    });
    return t;
 
}

互斥锁,模拟抢票,多个线程同时访问和改变一个变量

-(void)saleTicket
{
    while (1) {
        // 创建对象
        // self.obj = [[NSObject alloc]init];
        // 锁对象,本身就是一个对象,所以self就可以了
        // 锁定的时候,其他线程没有办法访问这段代码
        @synchronized (self) {
            // 模拟售票时间,我们让线程休息0.05s
            [NSThread sleepForTimeInterval:0.05];
            if (self.numTicket > 0) {
                self.numTicket -= 1;
                NSLog(@"%@卖出了一张票,还剩下%zd张票",[NSThread currentThread].name,self.numTicket);
            }else{
                NSLog(@"票已经卖完了");
                break;
            }
        }
        //使用NSLock对象也可以实现
        NSLock *lock = [[NSLock alloc]init];
        [lock lock];
        //Safe
        [lock unlock];
    }
}

如果将numTicket对象设置为atomic则默认会使用互斥锁,等同于@synchronized (self)
@property(automic,assign)NSInteger numTicket;

一个进程的内存分配

在这里插入图片描述

每个区域实际上都存储相应的内容,其中代码区、常量区、静态区这三个区域都是自动加载,并且在进程结束之后被系统释放,开发者并不需要进行关注。

栈区一般存放局部变量、临时变量,由编译器自动分配和释放,每个线程运行时都对应一个栈。而堆区用于动态内存的申请,由程序员分配和释放。一般来说,栈区由于被系统自动管理,速度更快,但是使用起来并不如堆区灵活。

对于 Swift 来说,值类型存于栈区,引用类型存于堆区。值类型典型的有 struct、enum 以及 tuple 都是值类型。而比如 Int、Double、Array,Dictionary 等其实都是用结构体实现的,也是值类型。而 class、closure 都是引用类型,也就是说 Swift 中我们如果遇到类和闭包,就要留个心眼,考虑一下他们的引用情况。

原文地址:https://blog.csdn.net/u010008647/article/details/122358585

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。

如若转载,请注明出处:http://www.7code.cn/show_51347.html

如若内容造成侵权/违法违规/事实不符,请联系代码007邮箱:suwngjj01@126.com进行投诉反馈,一经查实,立即删除!

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注