多线程

2016-11-23  本文已影响14人  七里田间的守望者

多线程原理:

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

主线程

多线程的实现方案

Paste_Image.png
@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(<#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]);
    });

主队列


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

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;
}
上一篇下一篇

猜你喜欢

热点阅读