iOS中的多线程(二)

/ 0评 / 0

在iOS中实现多线程有三种方法NSThread、NSOperation、GCD,下面我们先来看一下 NSThread

一、NSThread中的操作

创建、启动线程

 NSThread *thread = [[NSThread alloc] initWithTarget:self selector:@selector(run) object:nil];
// 线程一启动,就会在线程thread中执行self的run方法
[thread start];


[NSThread detachNewThreadSelector:@selector(run) toTarget:self withObject:nil]; //创建线程后自动启动线程
[self performSelectorInBackground:@selector(run) withObject:nil]; //隐式创建并启动线程
/**
上述2种创建线程方式的优缺点
	优点:简单快捷
	缺点:无法对线程进行更详细的设置
*/

//主线程相关用法
+ (NSThread *)mainThread; // 获得主线程
- (BOOL)isMainThread; // 是否为主线程
+ (BOOL)isMainThread; // 是否为主线程

//获得当前线程
NSThread *current = [NSThread currentThread];

//线程的调度优先级:调度优先级的取值范围是0.0 ~ 1.0,默认0.5,值越大,优先级越高
+ (double)threadPriority;
+ (BOOL)setThreadPriority:(double)p;

//设置线程的名字
- (void)setName:(NSString *)n;
- (NSString *)name;

二、下面我们看一下举例

首先我们看一下古老的方法创建thread

//古老的方式创建,要引入
- (void)test{
    UITextView *text = [[UITextView alloc] initWithFrame:CGRectMake(20, 130, 280, 80)];
    text.layer.borderWidth = .5;
    text.layer.borderColor = [UIColor blueColor].CGColor;
    text.layer.cornerRadius = 4;
    [self.view addSubview:text];
    
    UIButton *button = [[UIButton alloc] initWithFrame:CGRectMake(20, 64, 280, 50)];
    button.backgroundColor = [UIColor greenColor];
    [button addTarget:self action:@selector(btnClick) forControlEvents:UIControlEventTouchUpInside];
    [self.view addSubview:button];
}

- (void)btnClick{
    //1.获取当前线程
    NSThread *current=[NSThread currentThread];
    //主线程
    NSLog(@"btnClick----%@",current);
    //2.使用for循环执行一些耗时操作
    pthread_t thread;
    pthread_create(&thread, NULL, run, NULL);
}

void *run(void *data){
    //获取当前线程,是新创建出来的线程
    NSThread *current=[NSThread currentThread];
    for (int i=0; i<10000; i++) {
        NSLog(@"btnClick---%d---%@",i,current);
    }
    return NULL;
}

这里没有造成线程阻塞,在打印出线程数据的同时还可以对textView进行操作。
下面看一下NSThread创建的过程

//使用NSThread创建
- (void)test{
    UITextView *text = [[UITextView alloc] initWithFrame:CGRectMake(20, 130, 280, 80)];
    text.layer.borderWidth = .5;
    text.layer.borderColor = [UIColor blueColor].CGColor;
    text.layer.cornerRadius = 4;
    [self.view addSubview:text];
    
    UIButton *button = [[UIButton alloc] initWithFrame:CGRectMake(20, 64, 280, 50)];
    button.backgroundColor = [UIColor greenColor];
    [button addTarget:self action:@selector(btnClick) forControlEvents:UIControlEventTouchUpInside];
    [self.view addSubview:button];
}

-(void)btnClick{
    //1.获取当前线程
    NSThread *current=[NSThread currentThread];
    //主线程
    NSLog(@"btnClick----%@",current);
    //获取主线程的另外一种方式
    NSThread *main=[NSThread mainThread];
    NSLog(@"主线程-------%@",main);
    
    
    //2.执行一些耗时操作
    
    
    NSThread  *thread1=[[NSThread alloc]initWithTarget:self selector:@selector(run:) object:@"线程A"];
    //为线程设置一个名称
    thread1.name=@"线程A";
    //开启线程
    [thread1 start];

    
    
    NSThread  *thread2=[[NSThread alloc]initWithTarget:self selector:@selector(run:) object:@"线程B"];
    //为线程设置一个名称
    thread2.name=@"线程B";
    //开启线程
    [thread2 start];
    
    
    //创建完线程直接(自动)启动
    [NSThread detachNewThreadSelector:@selector(run:) toTarget:self withObject:@"创建完线程直接(自动)启动"];
    
    
    //在后台线程中执行===在子线程中执行
    [self performSelectorInBackground:@selector(run:) withObject:@"隐式创建"];
}


- (void)run:(NSString *)string{
    //获取当前线程
    NSThread *current=[NSThread currentThread];
    //打印输出
    for (int i=0; i<10; i++) {
        NSLog(@"run---%@---%@",current,string);
    }
}

三、线程间的通信

线程间通信:在1个进程中,线程往往不是孤立存在的,多个线程之间需要经常进行通信,1个线程传递数据给另1个线程,在1个线程中执行完特定任务后,转到另1个线程继续执行任务。

线程间通信常用方法
- (void)performSelectorOnMainThread:(SEL)aSelector withObject:(id)arg waitUntilDone:(BOOL)wait;
- (void)performSelector:(SEL)aSelector onThread:(NSThread *)thr withObject:(id)arg waitUntilDone:(BOOL)wait;

下面我们看一下一般图片下载的操作,当我们加载图片的时候都比较耽误时间,那我们就新建一个线程,然后在新线程中下载图片,回到主线程中刷新页面。

- (void)viewDidLoad{
    [super viewDidLoad];
    
    self.imageView = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, 320, 480)];
    [self.view addSubview:imageView];
    
    [NSThread detachNewThreadSelector:@selector(loadImage) toTarget:self withObject:nil];
    
    NSLog(@"11111111111");
}


- (void)loadImage{
    // 代码在主线程中执行的
    // 把网络上的图片 拿到本地data
    NSData *data = [NSData dataWithContentsOfURL:[NSURL URLWithString:@"http://f.hiphotos.baidu.com/image/pic/item/29381f30e924b8990c9439396c061d950a7bf603.jpg"]];
    
    // 是否等 主线程的方法执行过之后 再继续执行 分线程的代码
    [self performSelectorOnMainThread:@selector(showImage:) withObject:data waitUntilDone:YES];
    NSLog(@"222222222   %d", [NSThread isMainThread]);
}

- (void)showImage:(NSData *)data{
    self.imageView.image = [UIImage imageWithData:data];
    NSLog(@"33333333   %d", [NSThread isMainThread]);
}

四、多线程的安全隐患

资源共享,1块资源可能会被多个线程共享,也就是多个线程可能会访问同一块资源,比如多个线程访问同一个对象、同一个变量、同一个文件,当多个线程访问同一块资源时,很容易引发数据错乱和数据安全问题。
比如我们在银行存钱的同时取钱,再比如我们买票的时候几个人同时买可能就会出现这种问题,下面我们看一下问题代码。

- (void)test{
    a = 5;
    //开启多个线程,模拟售票员售票
    [NSThread detachNewThreadSelector:@selector(buyTickets1) toTarget:self withObject:nil];
    [NSThread detachNewThreadSelector:@selector(buyTickets2) toTarget:self withObject:nil];
}

// 线程同步

// 甲买5张票
- (void)buyTickets1{
        NSLog(@"甲 准备买5张票");
        if (a >= 5){
            NSLog(@"票至少有5张,可以给甲办理买票");
            a = a - 5;
            NSLog(@"甲成功购买5张票,还剩%d张",a);
        }else{
            NSLog(@"票不够5张,不能买");
        }
}

// 乙 买1 张票
- (void)buyTickets2{
    // 线程同步块
    NSLog(@"乙 准备买1张票");
    if (a >= 1){
        NSLog(@"票至少有1张,可以给乙办理买票");
        a = a - 1;
        NSLog(@"乙成功购买1张票,还剩%d张",a);
    }else{
        NSLog(@"票不够1张,不能买");
    }
}

来看打印结果,很多数都被买了多次,这怎么可能!!!

thread

五、多线程的安全隐患的解决办法:线程锁

那么我们怎么来解决这个问题呢?这里就为我们提供了一个工具---线程锁,当一个线程在对一个数据进行操作的时候会给这个线程添加一个“锁”锁住,不允许别的线程进行操作,等到这次的操作完成之后再解锁。其实线程锁有很多种,但是只能添加一把锁,我们这里就做一下演示。

- (void)test{
    a = 5;
    //开启多个线程,模拟售票员售票
    [NSThread detachNewThreadSelector:@selector(buyTickets1) toTarget:self withObject:nil];
    [NSThread detachNewThreadSelector:@selector(buyTickets2) toTarget:self withObject:nil];
    
    // 要用同一把锁
//    _lock = [[NSLock alloc] init];
}

// 线程同步

// 甲买5张票
- (void)buyTickets1{
    // 锁是一个标志
//        [_lock lock];
    @synchronized (self){
        NSLog(@"甲 准备买5张票");
        if (a >= 5){
            NSLog(@"票至少有5张,可以给甲办理买票");
            a = a - 5;
            NSLog(@"甲成功购买5张票,还剩%d张",a);
        }else{
            NSLog(@"票不够5张,不能买");
        }
    }
//        [_lock unlock];
}

// 乙 买1 张票
- (void)buyTickets2{
//    [_lock lock];
    @synchronized (self){
        // 线程同步块
        NSLog(@"乙 准备买1张票");
        if (a >= 1){
            NSLog(@"票至少有1张,可以给乙办理买票");
            a = a - 1;
            NSLog(@"乙成功购买1张票,还剩%d张",a);
        }else{
            NSLog(@"票不够1张,不能买");
        }
    }
//    [_lock unlock];
}

这里介绍了两种方法,一种是NSLock,另一种是自动的锁 synchronized,得到的结果都是一样的

thread1

评论已关闭。