多线程

多线程原理:

  • 同一时间CPU只能处理一条线程, 只有一条线程在工作
  • 多线程并发执行,起始是CPU在各个线程之间快速调度的结果
  • 由于CPU调度线程速度非常快,所以就造成了多线程并发的假象

一般情况下耗时操作放在子线程里面,多线程也正是解决耗时操作,防止卡住主线程产生的。

主线程

  • 程序已启动就自动创建的线程就是主线程
  • 作用一般就是相应用户点击事件,刷新UI等等

多线程的实现方案

Paste_Image.png
  • NSThread
@Parmark 第一中创建方法
    //创建线程
    //在内存中开辟空间
    NSThread * thread = [[NSThread alloc]initWithTarget:self selector:@selector(run:) object:@"thread"];
    thread.name = @"my-thread";
    //启动线程  并且系统会把这个线程放到可调度程序池里面 为了方便CPU来回调用
    [thread start];

//任务执行完毕后 会自动销毁线程
- (void)run:(NSString *)param
{
    //处理耗时操作
}

@Parmark 第二中创建方法

[NSThread detachNewThreadSelector:@selector(run:) toTarget:self withObject:@"thread"];

- (void)run:(NSString *)param
{
    //处理耗时操作
}

@Parmark 第三中创建方法

[self performSelectorInBackground:@selector(run:) withObject:@"thread"];

- (void)run:(NSString *)param
{
    //处理耗时操作
}


 //卡住线程睡两秒 控制线程进入阻塞状态
[NSThread sleepForTimeInterval:2.0];


//退出线程(强制性的)
[NSThread exit];

线程安全

  • 资源共享:一块资源 被多个线程共享 也就是多个线程访问同一块资源

隐患:

Paste_Image.png

解决引号 -- > 加把互斥锁

Paste_Image.png

代码:

###没加互斥锁的代码

    self.ticketCount = 100;//100张票
    
    self.thread1 = [[NSThread alloc]initWithTarget:self selector:@selector(saleTicket) object:nil];
    self.thread1.name = @"售票员1";
    
    self.thread2 = [[NSThread alloc]initWithTarget:self selector:@selector(saleTicket) object:nil];
    self.thread2.name = @"售票员2";
    
    self.thread3 = [[NSThread alloc]initWithTarget:self selector:@selector(saleTicket) object:nil];
    self.thread3.name = @"售票员3";

- (void)saleTicket
{
    while (1) {
        //先取出总数
        NSInteger count = self.ticketCount;
        if (count > 0) {
            
            self.ticketCount = count - 1;
            
            NSLog(@"%@卖了一张票 -- 还剩下%ld张票",[NSThread currentThread].name,_ticketCount);
        }else{
            NSLog(@"票已经卖完了");
            break;
        }
    }
}
###加互斥锁的代码
- (void)saleTicket
{
    //用同一把锁 可以记录线程的状态
    while (1) {
        
        @synchronized (self) {//加锁
            
            //先取出总数
            NSInteger count = self.ticketCount;
            if (count > 0) {
                
                self.ticketCount = count - 1;
                
                NSLog(@"%@卖了一张票 -- 还剩下%ld张票",[NSThread currentThread].name,_ticketCount);
            }else{
                NSLog(@"票已经卖完了");
                break;
            }
        }
        
    }
}

线程间的通讯


//开辟一个子线程
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
    [self performSelectorInBackground:@selector(downLoad) withObject:nil];
}


//子线程要做的事
- (void)downLoad
{
    NSURL * url = [NSURL URLWithString:@"http://f.hiphotos.baidu.com/image/pic/item/5ab5c9ea15ce36d358d27ee43ef33a87e850b114.jpg"];
    
    //下载图片
    NSData * data = [NSData dataWithContentsOfURL:url];
    
    //生成图片
    UIImage * image = [UIImage imageWithData:data];
    
    //回到主线程刷新UI
    [self.imageView performSelectorOnMainThread:@selector(setImage:) withObject:image waitUntilDone:YES];
    
//    [self performSelectorOnMainThread:@selector(showImage:) withObject:image waitUntilDone:YES];

}

GCD的基本使用

  • 任务: 执行操作
  • 队列: 存放任务
    • 队列的类型
    • 并发队列 (只有在)dispatch_async下才有效
    • 串行队列

同步 - 异步:主要影响是能不能开新线程

  • 同步只能在当前线程执行任务,不能开启新线程
  • 异步可以在新的线程中执行任务,能开启新线程

串行 - 并发:主要影响任务的执行方式

  • 串行:一个任务执行完毕 执行下一个任务
  • 并发:多个任务同时执行

使用步骤

  • 定制任务:确定想要做的事情
  • 将任务添加到队列中
    • GCD会自动将队列中的任务取出来,放到对应的线程中执行
    • 任务的去除遵循:先进先出 后进后出 的原则

代码:


//异步线程
    dispatch_async(<#dispatch_queue_t  _Nonnull queue#>, <#^(void)block#>)
    //同步线程
    dispatch_sync(<#dispatch_queue_t  _Nonnull queue#>, <#^(void)block#>)

###异步函数+并发队列 (可以开启多条线程 并且可以同时执行)
//创建一个并发队列  DISPATCH_QUEUE_CONCURRENT:队列类型
    dispatch_queue_t queue = dispatch_queue_create("com.baidu.queue", DISPATCH_QUEUE_CONCURRENT);
    
    //将任务加入队列
    dispatch_async(queue, ^{
       
        NSLog(@"%@",[NSThread currentThread]);
        
    });

#@pargam 或者这种写法

//获取全局队列
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    
    //将任务加入队列
    dispatch_async(queue, ^{
       
        NSLog(@"%@",[NSThread currentThread]);
        
    });


###同步函数+并发队列 (不会开启新的线程)

//获得全局的并发队列
    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    
    //同步函数
    dispatch_sync(queue, ^{
        NSLog(@"%@",[NSThread currentThread]);
    });


###异步函数+串行对列 (可以开线程 但是不能同时执行)
//串行队列 没有全局的 只能手动创建
    dispatch_queue_t queue = dispatch_queue_create("com.baidu.queue", DISPATCH_QUEUE_SERIAL);
    
    dispatch_async(queue, ^{
        NSLog(@"%@",[NSThread currentThread]);
    });

###同步函数+串行对列 (不可以开线程 )

 //串行队列 没有全局的 只能手动创建
    dispatch_queue_t queue = dispatch_queue_create("com.baidu.queue", DISPATCH_QUEUE_SERIAL);
    
    dispatch_sync(queue, ^{
        NSLog(@"%@",[NSThread currentThread]);
    });

主队列

  • GCD自带的一种特殊的串行队列
  • 放到主队列的任务都会,在主线程中执行
  • 使用dispatch_get_main_queue()获取主队列

###主队列+异步函数(只会在主线程中执行任务)

dispatch_queue_t queue = dispatch_get_main_queue();
    
    dispatch_async(queue, ^{
        
        NSLog(@"%@",[NSThread currentThread]);
        
    });

###主队列+同步函数(线程冲突 不会执行任何操作)

dispatch_queue_t queue = dispatch_get_main_queue();
    
    dispatch_sync(queue, ^{
        
        NSLog(@"%@",[NSThread currentThread]);
        
    });

各种队列的执行效果

Paste_Image.png

GCD的线程之间通讯


dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
       
        NSURL * url = [NSURL URLWithString:@"https://ss0.bdstatic.com/94oJfD_bAAcT8t7mm9GUKT-xh_/timg?image&quality=100&size=b4000_4000&sec=1479864304&di=99dcf40127f2dc4273536f73d0951638&src=http://d.hiphotos.baidu.com/image/pic/item/2e2eb9389b504fc2065e2bd2e1dde71191ef6de0.jpg"];
        NSData * data = [NSData dataWithContentsOfURL:url];
        
        UIImage * image = [UIImage imageWithData:data];
        
        //回到主线程
        dispatch_async(dispatch_get_main_queue(), ^{
           
            self.imageView.image = image;
            
        });
        
    });

GCD中还有另外一个执行任务的函数

//在它前面的函数执行完毕才执行它的任务,在它后面的函数在它执行完毕后才开始执行
dispatch_barrier_sync(<#dispatch_queue_t  _Nonnull queue#>, <#^(void)block#>)

iOS延时执行的函数

//延时两秒执行run方法
[self performSelector:@selector(run) withObject:nil afterDelay:2.0];


dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
        //执行的操作
        
    });

[NSTimer scheduledTimerWithTimeInterval:2.0 target:self selector:@selector(run) userInfo:nil repeats:NO];


static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        //一次性函数
        //此函数只执行一次
    });

GCD队列组

  • 用队列组下载多张图片并且合成一张图片

//创建一个队列
    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    
    //创建一个队列组
    dispatch_group_t group = dispatch_group_create();
    
    //1.下载图片1
    dispatch_group_async(group, queue, ^{
        
        NSURL * url = [NSURL URLWithString:@"http://d.hiphotos.baidu.com/image/h%3D200/sign=4241e02c86025aafcc3279cbcbecab8d/562c11dfa9ec8a13f075f10cf303918fa1ecc0eb.jpg"];
        NSData * data = [NSData dataWithContentsOfURL:url];
        
        UIImage * image = [UIImage imageWithData:data];
        
        self.image1 = image;
        
        
    });
    //2.下载图片2
    dispatch_group_async(group, queue, ^{
        
        
        NSURL * url = [NSURL URLWithString:@"http://e.hiphotos.baidu.com/image/pic/item/8cb1cb134954092359d94e479758d109b3de4952.jpg"];
        NSData * data = [NSData dataWithContentsOfURL:url];
        
        UIImage * image = [UIImage imageWithData:data];
        
        self.image2 = image;
        
        
    });
    //3.将图片1和图片2合成一张新的图片
    dispatch_group_notify(group, queue, ^{
        //能保证组里面的任务都完成了
        //能来到这里说明前两张图片一定下载完了
        
        //开启图形上下文
        UIGraphicsBeginImageContext(CGSizeMake(336, 440));
        
        //绘制图片
        [self.image1 drawInRect:CGRectMake(0, 0, 168, 220)];
        [self.image2 drawInRect:CGRectMake(168, 0, 168, 220)];
        
        //获取上下文的图片
        UIImage * image = UIGraphicsGetImageFromCurrentImageContext();
        
        //结束上下文
        UIGraphicsEndImageContext();
        
        //回到主线程显示图片
        dispatch_async(dispatch_get_main_queue(), ^{
           
            self.imageView.image = image;
            
        });
        
        
    });
    //4.将合成后的图片显示出来

GCD实现单利

###第一种方式

static Person * _person;
+ (instancetype)allocWithZone:(struct _NSZone *)zone
{
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        _instance = [super allocWithZone: zone];
    });
    return _instance;
}

+ (instancetype)defaultManger
{
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        
        _person = [[self alloc]init];
        
    });
    return _person;
}


//记得遵守NSCopying协议
//实现此方法为了保证copy的时候  访问的是同一个对象
- (id)copyWithZone:(NSZone *)zone
{
    return _person;
}


###第二种方式

static id _instance;
+ (instancetype)allocWithZone:(struct _NSZone *)zone
{
    @synchronized (self) {//加锁 防止多线程访问出问题
        if (_instance == nil)
        {
            _instance = [self allocWithZone:zone];
        }
    }
    return _instance;
}

+ (instancetype)sharedInstance
{
    @synchronized (self) {//加锁 防止多线程访问出问题
        if (_instance == nil)
        {
            _instance = [[self alloc]init];
        }
    }
    return _instance;
}

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

推荐阅读更多精彩内容