1.NSThread基本运用

app启动时默认都是,包括ViewController里执行的都是在主线程执行的
当计算了过大的时候会造成线程阻塞,比如点击按钮执行下面的方法,只有计算完,结束后,程序才能继续执行,如果是死循环的话,会一直占用主线程,导致主线程无法继续进行

-(void)pressBtn:(UIButton*)btn
{
    int i=0;
    while (true)
    {
        i++;
        NSLog(@"i=%d",i);
        if (i==10000)
        {
        //主线程,只有执行了10000次循环才会退出这个方法,继续向下执行
            break;
        }
    }

}

这样就需要开启另外一个线程,开启的方式有两种
第一种:alloc init 需要手动的start 和cancel

-(void)pressBtnMuti:(UIButton*)btnMuti
{
    //创建一个多线程对象

    NSThread*thread=[[NSThread alloc]initWithTarget:self selector:@selector(thread01) object:nil];
    //启动线程
    [thread start];
}

-(void)thread01
{
    int i=0;
    while (true)
    {
        i++;
        NSLog(@"in thread i=%d",i);
        [NSThread sleepForTimeInterval:1];
        if (i>=10000)
        {
            //直接结束当前的线程
            //+方法
            //会判断是哪一个线程来执行的这个函数
            //线程是由操作系统来管理
            //向操作系统发送通知,要求结束当前的进程
            [NSThread exit];


            //获取当前线程对象
            NSThread*curThread=[NSThread currentThread];
            //通知操作从系统来取消当前线程的执行
            [curThread cancel];
        }
    }
}

第二种创建方式,直接开启并执行的一个线程,执行完方法程自动关闭

-(void)pressBtn{
[NSThread detachNewThreadSelector:@selector(thread02) toTarget:self withObject:nil];
}
-(void)thread02{
    NSLog(@"线程二执行");
    }

2.线程执行顺序
当一个方法同上开启另个线程,是执行顺序是不确定的,又系统资源决定的

-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
    //开始执行线程1
    [NSThread detachNewThreadSelector:@selector(threadCount01) toTarget:self withObject:nil];
    //线程二:
     [NSThread detachNewThreadSelector:@selector(threadCount02) toTarget:self withObject:nil];
}

以下执行方法
conut1 final=200?
conut1 final=200?
不确定的,因为都是异步线程执行的方法,谁先执行完不确定,所以conut1 ,count2只有一个等于200,另一个不确定
而且每次run 结果极有可能不一样,考虑线程安全,需要上锁

-(void)threadCount01
{
    for (int i=0; i<100; i++)
    {
        //[_lock lock];
        _count++;
        //[_lock unlock];
        NSLog(@"Count=%d",_count);
    }

    NSLog(@"conut1 final=%d",_count);
}

-(void)threadCount02
{
    for (int i=0; i<100; i++)
    {
        [_lock lock];
        _count++;
        [_lock unlock];
        NSLog(@"Count=%d",_count);
    }

    NSLog(@"conut2 final=%d",_count);
}

同样的例子更加可以说明,线程是有操作系统资源决定

-(void)pressBtnMuti:(UIButton*)btn
{
    NSThread*threadAdd=[[NSThread alloc]initWithTarget:self selector:@selector(threadAdd) object:nil];
    [threadAdd start];

    [NSThread detachNewThreadSelector:@selector(threadMinus) toTarget:self withObject:nil];

}

-(void)threadAdd
{

    while (true)
    {
        [_lock lock];
        _value++;
        [_lock unlock];
         NSLog(@"value++=%d",_value);
      [NSThread sleepForTimeInterval:arc4random()%5];
         //[NSThread sleepForTimeInterval:1];
    }
}
-(void)threadMinus
{
    while (true)
    {
        [_lock lock];
        _value--;
        [_lock unlock];
        NSLog(@"value--=%d",_value);

        [NSThread sleepForTimeInterval:arc4random()%5];
         //[NSThread sleepForTimeInterval:1];
    }

}

执行结果等一段时间 value的值是不确定的

ios 解决线程阻塞问题 ios阻塞主线程_ViewUI

3.关于线程锁,多个线程对同一个数组进行访问,读写操作都要考虑加锁

//不考虑线程安全
-(void)addElment:(NSObject*)elem
{
    if (elem==nil)
        return ;
//        if ([_arrayQueue count]==0)
//        {
//            _arrayQueue=[[NSMutableArray alloc]init];
//        }
    if ([_arrayQueue count]>KMaxArrayQueue) {
        return ;
    }

    [_arrayQueue addObject:elem];

}
//不考虑线程安全
-(id)getElement
{

    if ([_arrayQueue count]==0)
    return nil;

    id obj=[_arrayQueue objectAtIndex:0];
    [_arrayQueue removeObjectAtIndex:_arrayQueue.count-1];
    return obj;
}


-(void)addElmentThreadSafty:(id)obj
{
    [_lock lock];
    [self addElment:obj];
    [_lock unlock];
}

-(id)getElementThreadSafty
{
    [_lock lock];
    id obj= [self getElement];
    [_lock unlock];
    return obj;
}

4.线程可以手动的取消

- (void)run
{
    for(int i = 0 ; i < 100 ; i++)
    {
        if([NSThread currentThread].isCancelled)
        {
            // 终止当前正在执行的线程
            [NSThread exit];
        }
        NSLog(@"-----%@----%d" , [NSThread currentThread].name, i);
        // 每执行一次,线程暂停0.5秒
        [NSThread sleepForTimeInterval:0.5];
    }
}
- (IBAction)cancelThread:(id)sender
{
    // 取消thread线程,调用该方法后,thread的isCancelled方法将会返回NO
    [thread cancel]; // ①
}

5.线程的优先级, 改变线程的优先级,可以改变输出顺序
优先级相同,输出不确定,当不一样的时候优先级高的for循环先执行完,然后执行另外优先级低的for循环

- (void)viewDidLoad
{
    [super viewDidLoad];
    NSLog(@"UI线程的优先级为:%g" , [NSThread threadPriority]);//0.5
    // 创建第一个线程对象
    NSThread* thread1 = [[NSThread alloc]
        initWithTarget:self selector:@selector(run) object:nil];
    // 设置第一个线程对象的名字
    thread1.name = @"线程A";
    NSLog(@"线程A的优先级为:%g" , thread1.threadPriority);
    // 设置使用最低优先级
    thread1.threadPriority = 1.0;
    // 创建第二个线程对象
    NSThread* thread2 = [[NSThread alloc]
        initWithTarget:self selector:@selector(run) object:nil];
    // 设置第二个线程对象的名字
    thread2.name = @"线程B";
    NSLog(@"线程B的优先级为:%g" , thread2.threadPriority);
    // 设置使用最高优先级
    thread2.threadPriority = 0;
    // 启动2个线程
    //改变线程的优先级,可以改变输出顺序
    [thread1 start];
    [thread2 start];
}
- (void)run
{
    for(int i = 0 ; i < 100 ; i++)
    {
        NSLog(@"-----%@----%d" , [NSThread currentThread].name, i);
    }
}

6.应用举例1
在一个循环(for i in 20)里开启另外一个线程,另外一个线程执行一个(for i in 5),请问打印结果?

- (void)viewDidLoad
{
[super viewDidLoad];
    NSLog(@"===%@" , [NSThread currentThread]);//其实就是主线程
   // ===<NSThread: 0x7fea61d130b0>{number = 1, name = main}
for(int i = 0 ; i < 20 ; i++) {
        NSLog(@"===%d"  , i);
        if(i == 5) {
            // 创建线程对象
            NSThread *thread = [[NSThread alloc]initWithTarget:self
                selector:@selector(run) object:nil];
            // 启动新线程
            NSLog(@"thread==%@",thread);
            //thread==<NSThread: 0x7facaa478610>{number = 2, name = main}
            [thread start];
//          // 创建并启动新线程
//          [NSThread detachNewThreadSelector:@selector(run) toTarget:self
//              withObject:nil];
        }
    }
}
- (void)run
{
    for(int i = 0 ; i < 5 ; i++) {
        NSLog(@"-----%@----%d" , [NSThread currentThread].name, i);
    }
}

打印结果:

ios 解决线程阻塞问题 ios阻塞主线程_ViewUI_02


如果再次run一下,打印结果为:

ios 解决线程阻塞问题 ios阻塞主线程_ui_03


为何不一样呢,因为开启的线程,不是在主线程,而且是异步执行,什么时候开始执行,取决于系统资源,所以会不一致

7.应用举例2
利用NSThread如何实现适时更改UIProgressView的进度条呢?,
方法:开启一个异步线程,执行方法改变progress,执行主线程方法,改变UI,属于异步执行,同步UI

//使用主线程来调用函数
-(void)pressMian{
    //参数二:函数中的参数
    //参数三:是否等待主线程空余时执行
    //如果为NO 不需要等待
    [self performSelectorOnMainThread:@selector(updateProgressMain:) withObject:_progressView waitUntilDone:NO];
}

//主线程主使用
//方法一:
-(void)updateProgressMain:(UIProgressView*)pView
{
    while (_progressView.progress<1)
    {
        _progressView.progress+=0.001;
        NSLog(@"_progress=%f",_progressView.progress);

    }

}

#pragma mark 开启一个异步线程,执行方法改变progress,执行主线程方法,改变UI,属于异步执行,同步UI
//方法二:
-(void)pressMian2
{
    [NSThread detachNewThreadSelector:@selector(updateProgress02) toTarget:self withObject:nil];

}

-(void)updateProgress02
{
    while (_curProgress<=1){
        _curProgress+=0.0001;
        //获得主线程
        NSThread*mainT=[NSThread mainThread];

        //执行一个函数,指定某个线程来执行某个函数
        [self performSelectorOnMainThread:@selector(changeProgress) withObject:mainT waitUntilDone:YES];
    }
}

//主线程中执行函数
-(void)changeProgress
{
    _progressView.progress=_curProgress;
        NSLog(@"_progress=%f",_progressView.progress);
    if (_progressView.progress==1)
    {
        _progressView.progress=0;
        return;
    }
}

#pragma mark 开启普通线程,进度条会在progress=1是突然出现
//开启普通线程
-(void)pressThread{
    NSThread *thread=[[NSThread alloc]initWithTarget:self selector:@selector(updateProgress) object:nil];
    [thread start];
}
//普通线程函数
-(void)updateProgress {
    while  (_progressView.progress<1.0f){
        _progressView.progress+=0.0001;
            NSLog(@"_progress=%f",_progressView.progress);
        if (_progressView.progress==1){
            return;
        }
    }
}

8应用举例3
异步下载一张图片,同步更新Image

- (IBAction)showImage:(id)sender
{
    NSString* url = @"http://www.crazyit.org/logo.jpg";
    // 创建新线程对象
    NSThread *thread = [[NSThread alloc]initWithTarget:self
        selector:@selector(downloadImageFromURL:) object:url];
    // 启动线程
    [thread start];
}
// 定义一个方法作为线程执行体。
-(void)downloadImageFromURL:(NSString *) url
{
    // 从网络获取数据
    NSData *data = [[NSData alloc]
        initWithContentsOfURL:[NSURL URLWithString:url]];
    // 将网络数据初始化为UIImage对象
    UIImage *image = [[UIImage alloc]initWithData:data];
    if(image != nil)
    {
        // 在主线程中执行updateUI:方法
        [self performSelectorOnMainThread:@selector(updateUI:)
            withObject:image waitUntilDone:YES]; // ①
    }
    else
    {
        NSLog(@"---下载图片出现错误---");
    }
}
-(void)updateUI:(UIImage*) image
{
    self.iv.image = image;
}
<script type="text/javascript"> $(function () { $('pre.prettyprint code').each(function () { var lines = $(this).text().split('\n').length; var $numbering = $('<ul/>').addClass('pre-numbering').hide(); $(this).addClass('has-numbering').parent().append($numbering); for (i = 1; i <= lines; i++) { $numbering.append($('<li/>').text(i)); }; $numbering.fadeIn(1700); }); }); </script>