2013-12-24 17:04:32 u012884714 阅读数 3593

线程 NSThread  多线程


线程与进程
进程
 是一个活动的程序,一个容器
 是系统资源管理的最小单位
 切换代价较高
 
线程
是在进程容器中运行,实际工作的代码
是程序执行的最小单位
切换代价较低(由于线程是在同一块内存中进行切换的)

一个NSThread对象在程序中控制一个线程,当你有很多任务需要执行,但又不想阻塞主线程时,
多线程将会很有用。运用多线程可以同时执行多个任务。

NSThread的方法
//初始化
1、- (id)init  //这个是NSThread的指定初始化方法
2、- (id)initWithTarget:(id)target selector:(SEL)selector object:(id)argument
初始化一个线程,应该注意的是这个SEL只能有一个参数,并且选择的方法不能有返回值,
需要注意的是这个方法只是创建了一个线程,并没有开始这个线程,要用stat方法去开始线程

3、+ (void)detachNewThreadSelector:(SEL)aSelector toTarget:(id)aTarget withObject:(id)anArgument
创建并开始一个线程。
4、- (void)start   //开始一个线程
5、+ (void)sleepUntilDate:(NSDate *)aDate
阻塞当前线程,直到指定时间
6、+ (void)sleepForTimeInterval:(NSTimeInterval)ti
使线程睡眠一段时间

//判断线程状态
7、- (BOOL)isExecuting   //判断线程是否正在执行
8、- (BOOL)isCancelled  //判断线程是否已经取消
9、- (BOOL)isFinished  //判断线程是否执行完
10、+ (BOOL)isMainThread  //判断当前线程是不是主线程
11、+ (BOOL)isMultiThreaded  //判断应用程序是否是多线程的
12、- (BOOL)isMainThread  //判断制定的对象是否是主线程


13、+ (NSThread *)mainThread  //返回一个主线程对象
14、+ (NSThread *)currentThread  //返回当前的线程
15、+ (NSArray *)callStackReturnAddresses
返回的是这个线程在栈中所占的地址所组成的数组
16、+ (NSArray *)callStackSymbols  //返回栈空间的符号


//线程属性

17、– threadDictionary  //返回线程对象的字典
18、– name  //返回线程名字
19、– setName:  //设置线程名字
20、– stackSize  //返回线程所占栈的空间大小
21、– setStackSize:   //设置线程所占栈的空间大小


//线程优先权
22、+ threadPriority  //返回当前线程的优先权,其实返回的是一个double型数字,
 //从0.0 到1.0 其中1.0最高
23、– threadPriority  //返回当前线程对象的优先权
24、+ setThreadPriority: //设置当前线程的有限权

25、– setThreadPriority:  //同上


写给自己不多说。——LC





2016-03-03 17:32:48 wei78008023 阅读数 1102

今天闲来无事,就来总结一下iOS中的多线程开发吧。


iOS有三种多线程编程的技术,分别是:

1.NSThread------每个NSThread对象对应一个线程,量级较轻(真正的多线程)

2.NSOperation------NSOperation/NSOperationQueue 面向对象的线程技术

3.GCD------Grand Central Dispatch(派发) 是基于C语言的框架,可以充分利用多核,是苹果推荐使用的多线程技术

其中,NSOperation和GCD是苹果专门开发的“并发”技术,使得程序员可以不再去关心线程的具体使用问题。

以上这三种编程方式从上到下,抽象度层次是从低到高的,抽象度越高的使用越简单,也是Apple最推荐使用的,在项目中很多框架技术分别使用了不同多线程技术。


三种多线程技术的对比

1.NSThread
优点:NSThread 比其他两个轻量级
缺点:需要自己管理线程的生命周期、线程同步、睡眠以及唤醒。线程同步对数据的加锁会有一定的系统开销

2.Cocoa  NSOperation
优点:不需要关心线程管理, 数据同步的事情,可以把精力放在自己需要执行的操作上。
Cocoa operation相关的类是NSOperation, NSOperationQueue.
NSOperation是个抽象类,使用它必须用它的子类,可以实现它或者使用它定义好的两个子类: NSInvocationOperation和NSBlockOperation.创建NSOperation子类的对象,把对象添加到NSOperationQueue队列里执行。

--NSOperation是面向对象的

3.GCD(全优点)
Grand Central dispatch(GCD)是Apple开发的一个多核编程的解决方案。在iOS4.0开始之后才能使用。GCD是一个替代NSThread, NSOperationQueue,NSInvocationOperation等技术的很高效强大的技术。

--GCD是基于C语言的,执行效率高


三种多线程技术的实现

一、NSThread的使用(基本已过时)

1.创建使用线程:

类方法直接开启后台线程,并执行选择器方法

// 新建一个线程,调用@selector方法
[NSThread detachNewThreadSelector:@selector(bigDemo) toTarget:self withObject:nil];
成员方法,在实例化线程对象之后,需要使用start执行选择器方法
NSThread *thread = [[NSThread alloc] initWithTarget:self selector:@selector(bigDemo) object:nil];
// 启动start线程
[thread start];
对于NSThread的简单使用,可以用NSObject的performSelectorInBackground替代
// performSelectorInBackground是将bigDemo的任务放在后台线程中执行
[self performSelectorInBackground:@selector(bigDemo) withObject:nil];

或者

//在指定线程中执行,但该线程必须具备run loop。
- (void)performSelector:(SEL)aSelector onThread:(NSThread *)thr withObject:(id)arg waitUntilDone:(BOOL)wait; 

2.线程中代码执行完成后,需要回调主线程才能更新UI

//第一种方式
//[self performSelectorOnMainThread:@selector(settingImage:) withObject:image waitUntilDone:NO];
//第二种方式
//[self.imageView performSelector:@selector(setImage:) onThread:[NSThread mainThread] withObject:image waitUntilDone:NO];
//第三种方式
[self.iconView performSelectorOnMainThread:@selector(setImage:) withObject:image waitUntilDone:NO];
3.其它一些方法
+ (NSThread *)currentThread; //获得当前线程
+ (void)sleepForTimeInterval:(NSTimeInterval)ti; //线程休眠
+ (NSThread *)mainThread; //主线程,亦即UI线程了
- (BOOL)isMainThread; + (BOOL)isMainThread; //当前线程是否主线程
- (BOOL)isExecuting; //线程是否正在运行
- (BOOL)isFinished; //线程是否已结束
4.线程之间的同步(多个线程访问同一资源时)

涉及到线程间同步仍然需要配合使用NSLock,NSCondition 或者 @synchronized

例子一(NSLock)

-(void)run{  
    [self.lock lock];//给lock锁上,这里可以通俗地理解为一间房一次只能进一个人,每个进去时把门反锁,出来时把门打开  
    if(self.count > 0){  
        self.count--;  
        NSLog(@"%@抢到一张火车票", [[NSThread currentThread] name]);  
    }  
    [self.lock unlock];//解锁  
}

例子二(@synchronized)

@synchronized(self){  
    if(self.count > 0){  
       self.count--;  
        NSLog(@"%@抢到一张火车票", [[NSThread currentThread] name]);  
    }  
} 

例子三(NSCondition)

[_condition lock]; 
if(self.count > 0){  
       self.count--;  
        NSLog(@"%@抢到一张火车票", [[NSThread currentThread] name]);  
    } 
[_condition signal];  
[_condition unlock];


二、NSOperation,面向对象的多线程技

使用步骤:

1.实例化操作

// 实例化操作队列
_queue = [[NSOperationQueue alloc] init];
2.NSInvocationOperation例子
NSInvocationOperation *op1 = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(opAction) object:nil];
// 如果使用start,会在当前线程启动操作
// [op1 start];
// 一旦将操作添加到操作队列,操作就会启动
[_queue addOperation:op1];
3.NSBlockOperation例子
- (IBAction)operationDemo3:(id)sender
{
     //下载
     NSBlockOperation *op1 = [NSBlockOperation blockOperationWithBlock:^{
         NSLog(@"下载 %@" , [NSThread currentThread]);
     }];
     //滤镜
     NSBlockOperation *op2 = [NSBlockOperation blockOperationWithBlock:^{
         NSLog(@"滤镜 %@" , [NSThread currentThread]);
     }];
     //显示
     NSBlockOperation *op3 = [NSBlockOperation blockOperationWithBlock:^{
         NSLog(@"更新UI %@" , [NSThread currentThread]);
     }];
     
     // 添加操作之间的依赖关系,所谓“依赖”关系,就是等待前一个任务完成后,后一个任务才能启动
     // 依赖关系可以跨线程队列实现
     // 提示:在指定依赖关系时,注意不要循环依赖,否则不工作。
     [op2 addDependency:op1];
     [op3 addDependency:op2];
     //[op1 addDependency:op3];
     
     [_queue addOperation:op1];
     [_queue addOperation:op2];
     [[NSOperationQueue mainQueue] addOperation:op3];
}

4.其它控制介绍

a.将操作添加到队列NSOperationQueue即可启动多线程执行

[_queue addOperation:op1];
[_queue addOperation:op2];
b.更新UI使用主线程队列
//两方式
[NSOpeationQueue mainQueue] addOperation ^{
};
[[NSOperationQueue mainQueue] addOperation:op3];
c.操作队列的setMaxConcurrentOperationCount,可以设置同时并发的线程数量!
[_queue setMaxConcurrentOperationCount:2];
提示:此功能仅有NSOperation有!
d.使用addDependency可以设置任务的执行先后顺序,同时可以跨操作队列指定依赖关系
// 添加操作之间的依赖关系,所谓“依赖”关系,就是等待前一个任务完成后,后一个任务才能启动
// 依赖关系可以跨线程队列实现
// 提示:在指定依赖关系时,注意不要循环依赖,否则不工作。
[op2 addDependency:op1];
[op3 addDependency:op2];
[op1 addDependency:op3];
提示:在指定依赖关系时,注意不要循环依赖,否则不工作。


三、GCD(C语言)

GCD就是为了在“多核”上使用多线程技术,要使用GCD,所有的方法都是dispatch开头的

名词解释:
global全局
queue队列
async异步---把代码块提交给队列后,立即返回
sync同步---把代码块提交给队列后要等代码块执行完才返回,阻塞当前线程

1.关于GCD的队列

a.全局队列(global)--队列内部并行无序(+async异步 +sync同步)

参数:优先级 DISPATCH_QUEUE_PRIORITY_DEFAULT 始终是 0

dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);

b.串行队列(Serial)--队列内部串行

dispatch_queue_t queue = dispatch_queue_create("myQueue", DISPATCH_QUEUE_SERIAL);

是创建得到的,不能直接获取,当你创建多个Serial queue时,虽然它们各自内部是串行执行的,但Serial queue与Serial queue之间是并发执行的。

c.并行队列(Concurrent)--队列内部并行FIFO(+async异步 +sync同步)

dispatch_queue_t queue = dispatch_queue_create("queueName", DISPATCH_QUEUE_CONCURRENT)

d.主队列(Main)--它是全局可用的serial queue,它是在应用程序主线程上执行任务的,只能同步执行

dispatch_async(dispatch_get_main_queue(), ^{
        NSLog(@"main - > %@", [NSThread currentThread]);
});

2.GCD简单应用例子

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{  
     NSURL * url = [NSURL URLWithString:<span>kURL</span>];  
     NSData * data = [[NSData alloc]initWithContentsOfURL:url];  
     UIImage *image = [[UIImage alloc]initWithData:data];  
          
         if (data != nil) {  
             dispatch_async(dispatch_get_main_queue(), ^{  
             _imageView.image = image;  
            });  
         }  
     });

3.GCD的队列应用

a.创建串行队列提交同步任务

dispatch_queue_t queue = dispatch_queue_create("queueName", DISPATCH_QUEUE_SERIAL);
    dispatch_sync(queue, ^{
        //code 任务一
    });
    dispatch_sync(queue, ^{
        //code 任务二
    });
队列中的任务是串行出列的,任务一执行结束后执行任务二,然而sync又是同步任务,会阻塞当前线程,实际上这里造成了死锁。
b.自定义串行队列,提交异步任务
dispatch_queue_t queue = dispatch_queue_create("queueName", DISPATCH_QUEUE_SERIAL);   
    dispatch_async(queue, ^{
        //code 任务一
    });
    dispatch_async(queue, ^{
        //code 任务二
    });
队列的任务是串行出列,任务一执行结束后执行任务二。

c.自定义并行队列,提交同步任务

dispatch_queue_t queue = dispatch_queue_create("queueName", DISPATCH_QUEUE_CONCURRENT);
    dispatch_sync(queue, ^{
        //code 任务一
    });
    dispatch_sync(queue, ^{
        //code 任务二
    });
队列的任务是并行出列,顺序按先进先出的顺序执行,既任务一出列后任务二接着出列(但任务二与任务一又是同步的,一般不这么用)
d.自定义并行队列,提交异步任务

dispatch_queue_t queue = dispatch_queue_create("queueName", DISPATCH_QUEUE_CONCURRENT);
    dispatch_async(queue, ^{
        //code 任务一
    });
    dispatch_async(queue, ^{
        //code 任务一
    });
任务一出列后任务二出列,各任务之间是异步的,不会阻塞当前线程。

e.在主队列提交同步任务,阻塞主线程,造成死锁!

dispatch_sync(dispatch_get_main_queue(), ^{
        //code
    });
f.在主队列提交异步任务
dispatch_async(dispatch_get_main_queue(), ^{
        //code任务
    });
g.在全局队列提交同步任务
dispatch_sync(dispatch_get_global_queue(0, 0), ^{
     //code
 });
h.在全局队列提交异步任务
dispatch_async(dispatch_get_global_queue(0, 0), ^{
        //code
    });
dispatch_get_gloabal_queue 的第一个参数为枚举类型(默认为0),决定任务的优先级 ,第二个参数为Apple保留参数,传0

i.总结

队列的类型决定了队列任务的执行方式(主队列是一个串行队列)。一般把会阻塞主线程的任务提交到异步并行队列当中,防止死锁形成。

4.GCD的dispatch_group_async使用

实现监听一组任务是否完成,完成后得到通知执行其他的操作。这个方法很有用,比如你执行三个下载任务,当三个任务都下载完成后你才通知界面说完成的了。

//此方法可以实现监听一组任务是否完成,如果完成后通知其他操作(如界面更新),此方法在下载附件时挺有用,  
     //在搪行几个下载任务时,当下载完成后通过dispatch_group_notify通知主线程下载完成并更新相应界面  
     dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);  
     dispatch_group_t group = dispatch_group_create();  
     dispatch_group_async(group, queue, ^{  
         [NSThread sleepForTimeInterval:0.09];  
       
     NSLog(@"group1");  
     NSURL * url = [NSURL URLWithString:kURL];  
     NSData * data = [[NSData alloc]initWithContentsOfURL:url];  
     _image = [[UIImage alloc]initWithData:data];  
       
     });  
     dispatch_group_async(group, queue, ^{  
         [NSThread sleepForTimeInterval:0.09];  
         NSLog(@"group2");  
     });  
     dispatch_group_async(group, queue, ^{  
         [NSThread sleepForTimeInterval:0.09];  
         NSLog(@"group3");  
     });  
       
     dispatch_group_notify(group, dispatch_get_main_queue(), ^{  
         NSLog(@"updateUi");  
       
         _imageView.image = _image;  
     });
5.GCD的dispatch_barrier_async使用
- (void)viewDidLoad  
{  
    [super viewDidLoad];  
      
    //是在前面的任务执行结束后它才执行,而且它后面的任务等它执行完成之后才会执行  
    dispatch_queue_t queue = dispatch_queue_create("gcd.devdiy.com", DISPATCH_QUEUE_CONCURRENT);  
    dispatch_async(queue, ^{  
        [NSThread sleepForTimeInterval:2];  
        NSLog(@"dispatch_async1");  
    });  
      
    dispatch_async(queue, ^{  
        [NSThread sleepForTimeInterval:4];  
        NSLog(@"dispatch_async2");  
    });  
      
    dispatch_barrier_async(queue, ^{  
        NSLog(@"dispatch_barrier_async");  
        [NSThread sleepForTimeInterval:4];  
    });  
      
    dispatch_async(queue, ^{  
        [NSThread sleepForTimeInterval:1];  
        NSLog(@"dispatch_async");  
    });  
}   


线程与进程的区别

进程和线程都是由操作系统所体会的程序运行的基本单元,系统利用该基本单元实现系统对应用的并发性。
进程和线程的主要差别在于它们是不同的操作系统资源管理方式。
进程有独立的地址空间,一个进程崩溃后,在保护模式下不会对其它进程产生影响,而线程只是一个进程中的不同执行路径。
线程有自己的堆栈和局部变量,但线程之间没有单独的地址空间,一个线程死掉就等于整个进程死掉。
所以多进程的程序要比多线程的程序健壮,但在进程切换时,耗费资源较大,效率要差一些。
但对于一些要求同时进行并且又要共享某些变量的并发操作,只能用线程,不能用进程。






2017-01-11 23:01:12 bobbob32 阅读数 1131

关于IOS多线程

1.线程的概念

1个cpu执行的cpu命令列为一条无分叉路径,即为线程
如果这种无分叉路径有多条,这就是多线程
在学习操作系统的时候学过,cpu的抢占机制–时间片轮转
所以多线程程序的某个线程可以和其它多个线程反复进行上下文切换,这就看上去像1个cpu核能够并列执行多个线程一样

2.多线程导致的一些问题

  1. 数据竞争问题:比如一个线程修改数据而其它一个线程这个相同时刻访问这个数据,这就导致数据竞争
  2. 死锁问题:导致死锁原因, 互斥条件:一个资源每次只能被一个进程使用。请求与保持条件:一个进程因请求资源而阻塞时,对已获得的资源保持不放。不剥夺条件:进程已获得的资源,在末使用完之前,不能强行剥夺。 循环等待条件:若干进程之间形成一种头尾相接的循环等待资源关系。
  3. 太多线程比较消耗内存:例如:cpu寄存器在时间片轮转时候,等待唤醒的线程会将cpu的寄存器的信息保存到各自专用的内存块中,一旦唤醒,立刻复原cpu寄存器等信息。

多线程的优点也是大于缺点的,在执行长时间的处理时仍能保证用户界面的响应性能

3.GCD中的同步(synchronize)和异步(asynchronize)

//不会阻塞主线程,异步的,可以先执行后面代码
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        
    });
    //会阻塞主线程等待block中执行完毕
    dispatch_sync(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        
    });

4.GCD的并行(concurrent)和串行(serial)

  1. 串行队列:会等待现在处理结束,也就是队列中的任务顺序执行,等待前一个任务结束,才开始下一个任务
  2. 并行队列:不等待现在执行处理结束,就是队列中所有任务都立刻开始执行,没有先后顺序
//串行队列
    dispatch_queue_t serial_queue = dispatch_queue_create("seral", DISPATCH_QUEUE_SERIAL);
    //主队列 属于串行的队列 serial,由于主线程只有一个,所以它是串行的,可以思考一下
    dispatch_queue_t main_queue = dispatch_get_main_queue();
//异步 全局(并行)
    dispatch_async(global_queue_height, ^{
        //并行任务
        dispatch_async(main_queue, ^{
            //主队列任务
        });
    });
    //主线程同步会出现死锁,阻塞主线程,所以只能使用主线程异步
dispatch_async(main_queue, ^{
            //主队列任务
        });

并行队列

//全局队列 属于并行队列 concurrent
    dispatch_queue_t global_queue_height = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH,0);
    //并行队列
    dispatch_queue_t concurrnt_queue = dispatch_queue_create("concurrent", DISPATCH_QUEUE_CONCURRENT);
```c
    全局队列和主队列是IOS自定的两个队列
    dispatch_queue_t global_queue_height = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH,0);
   全局队列的优先级 DISPATCH_QUEUE_PRIORITY_HIGH,DISPATCH_QUEUE_PRIORITY_LOW,DISPATCH_QUEUE_PRIORITY_DEFAULT,DISPATCH_QUEUE_PRIORITY_BACKGROUND有四种种优先级
## 5.dispatch_set_target_queue 变更两个队列的优先级方法 ##

## 6.dispatch_after ##
并不是在指定时间后执行处理,而是到指定时间追加处理到队列(queue)中去执行
在下面例子中由于在主队列(main queue)的Runloop中执行。
第二个参数也可以指定具体某时某刻
```c
 dispatch_time_t outtime = dispatch_time(DISPATCH_TIME_NOW, 15ull*NSEC_PER_SEC);
    dispatch_after(outtime, dispatch_get_main_queue(), ^{
       //15ull后添加到队列中
    });

7.关于DISPATCH_GROUP

追加到队列中的任务如果想在结束之后进行处理,可以将其放入group中

  1. 队列执行完之后会调用dispatch_group_notify函数
  2. 也可以使用dispatch_group_wait设置超时
  3. 也可以使用dispatch_barrier_async设置执行到某个位置中断,等待前面执行完,再执行后面的队列
  4. dispatch_apply该函数按照指定次数将指定的block追加到指定的队列(queue)中并等待全部处理结束 例子中NSLog(@"%zu",index);将添加到global_queue1中执行10次,等待结束后执行后面代码
  5. dispatch_suspend/dispatch_resume在追加大量的任务,有时候希望不执行这个任务就让其挂起/…恢复
  6. dispatch_once这个在单例模式中可以使用,当然也可以代替懒汉加载模式,保证程序在应用执行中只执行一次
  7. dispatch_I/O读取/写入 较大文件并列读取(写入),提高读(写)取速度
  8. dispatch semaphore解决数据不同步的一些问题的,进行排它控制
dispatch_group_t group_t = dispatch_group_create();
    dispatch_group_async(group_t, queue_global12, ^{
        
    });
    dispatch_group_async(group_t, queue_global12, ^{
        
    });
    //队列执行完成后调用
    dispatch_group_notify(group_t, queue_global12, ^{
        
    });
    dispatch_group_wait(group_t, DISPATCH_TIME_FOREVER);
    
    dispatch_time_t mytime = dispatch_time(DISPATCH_TIME_NOW, 1ull*NSEC_PER_SEC);
   long result =  dispatch_group_wait(group_t, mytime);
    if (result==0) {
       //group中的全部处理结束
    }else{
        //group的某一个处理还在进行中
    }
    dispatch_barrier_sync(global_queue1, ^{
        //等到追加到concurrent dispatch queue 执行完毕之后才将制定的处理追加到指定的queue
    });
    dispatch_apply(10, global_queue1, ^(size_t index) {
        NSLog(@"%zu",index);
    });
    //挂起
     dispatch_suspend(global_queue1);
    // 恢复
dispatch_resume(global_queue1);
   
dispatch_group_t group = dispatch_group_create();
dispatch_group_async(group, dispatch_get_global_queue(0,0), ^{
     // 并行执行的线程1
});
dispatch_group_async(group, dispatch_get_global_queue(0,0), ^{
     // 并行执行的线程2
});
dispatch_group_notify(group, dispatch_get_global_queue(0,0), ^{
     // 两个线程都执行完
});

最后关于一个缓存类,缓存有个特点数据频繁读取,写入次数有限,如果使用互斥步锁(NSLock,@synichronized,dispatch_semaphore_t等)资源消耗比较多,可以了解读写锁相关的内容
使用dispatch_queue_create创建的队列在并行多任务情况下,dispatch_barrier_async函数提交的任务会等它前面的任务执行完才开始,然后它后面的任务必须等它执行完毕才能开始.

@interface ICatche : NSObject
+(instancetype)sharedCatch;
- (id)cacheWithKey:(id)key;
-(void)setCatcheObject:(id)obj withKey:(id)key;
@end

#import "ICatche.h"
@interface ICatche()
{
    dispatch_queue_t queue;
}
@property(nonatomic,strong)NSMutableDictionary *tempDic;
@end
@implementation ICatche
static ICatche *instance;
+(instancetype)sharedCatch{
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        instance = [[self alloc]init];
    });
    return instance;
}
+(instancetype)allocWithZone:(struct _NSZone *)zone{
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        instance = [super allocWithZone:zone];
    });
    return instance;
}
-(instancetype)init{
    if (self=[super init]) {
        queue = dispatch_queue_create("Icatch_queue", DISPATCH_QUEUE_CONCURRENT);
        self.tempDic = [NSMutableDictionary dictionaryWithCapacity:20];
    }
    return self;
}
//读取数据
- (id)cacheWithKey:(id)key{
    __block id obj;
    dispatch_sync(queue, ^{
        obj = [self.tempDic objectForKey:key];
    });
    return obj;
}
//写入数据
-(void)setCatcheObject:(id)obj withKey:(id)key{
    dispatch_barrier_async(queue, ^{
        [self.tempDic setObject:obj forKey:key];
    });
}
@end

大神唐巧文章传送门

[NSThread的用法](https://www.jianshu.com/p/686dbf4bbb52)
2018-05-14 15:34:25 PDD_1128 阅读数 74

    在同一时刻,一个CPU只能处理1条线程,但CPU可以在多条线程之间快速的切换,只要切换的足够快,就造成了多线程一同执行的假象。

    多线程是通过提高资源使用率来提高系统总体的效率。

    主线程:处理UI,所有更新UI的操作都必须在主线程上执行。不要把耗时操作放在主线程,会卡界面。

    运用多线程的目的是:将耗时的操作放在后台执行!

一.线程的生命周期:

    新建- 就绪- 运行- 阻塞- 死亡

1.新建:实例化线程对象

2.就绪:详线程对象发送start消息,线程对象被加入可调度线程池等待CPU调度。

3.运行:CPU负责调度可调度线程池中线程的执行。线程执行完成之前,状态可能会在就绪和运行之间来回切换。就绪和运行之间的状态变化由CPU负责,程序员不能干预。

4.阻塞:当满足某个预定条件时,可以使用休眠或锁,阻塞线程执行。sleepForInterval(休眠指定时长),sleepUntilData(休眠到指定日期),@synchronized(self)(互斥锁)

5.死亡:正常死亡,线程执行完毕。非正常死亡,当满足某个条件后,在线程内部中止执行/在主线程中止线程对象。

二.多线程的几种方式:

    PThread:

一套C语言的通用的多线程API,适用于Unix/Linux/Windows等系统,跨平台,可移植。使用难度比较大,内存自己管理。

 

    NSThread:

OC语言的,使用更加面向对象,简单易用,可直接操作线程对象。不太常用,自己管理内存。

三种创建方式

    init方式,

   detachNewThreadSelector创建好之后自动启动,

   performSelectorInBackground创建好之后也是直接启动

类方法:

   [NSThread currentThread];                //当前线程

   [NSThread sleepForTimeInterval:2];         //休眠多久

   [NSThread sleepUntilDate:[NSDate date]];   //休眠到指定时间

   [NSThread exit];                          //退出线程

   [NSThread isMainThread];             //判断当前线程是否为主线程

   [NSThread isMultiThreaded];           //判断当前线程是否是多线程

   属性:

   thread.isExecuting; //线程是否在执行

   thread.isCancelled; //线程是否被取消

   thread.isFinished; //线程是否完成

thread.isMainThread; //是否是主线程

   thread.threadPriority; //线程的优先级,取值范围0.01.0,默认优先级0.51.0表示最高优先级,优先级高,CPU调度的频率高

 

    GCD:

C语言的,GCD会自动利用更多的CPU内核,GCD自动管理线程的生命周期,例如创建线程,调度任务,销毁线程等,只要告诉GCD想要如何执行什么任务,不需要编写任何线程管理代码,易用。

队列创建方法:

   dispatch_queue_create(“队列标示”,“DISPATCH_QUEUE_SERIAL

串行或DISPATCH_QUEUE_CONCURRENT并行”)

   dispatch_get_main_queue()  // 回到主线程进行UI操作

   dispatch_get_global_queue(0, 0)  //全局并发队列,第一个参数是优先级。

任务执行方式:

同步:dispatch_sync

异步:dispatch_async

应用:

1.串行同步:在主线程上,不开启新线程。

2.串行异步:开启一条新线程,顺序执行。

3.并发同步:主线程上,不开启新线程。

4.并发异步:开启多条线程,无顺序执行。

5.主队列同步:发生死锁,程序崩溃。

       原因:如果在主线程中运用主队列同步,也就是把任务放到了主线程的队列中。而同步对于任务是立刻执行的,那么当把第一个任务放进主队列时,它就会立马执行。可是主线程现在正在处理syncMain方法,任务需要等syncMain执行完才能执行。syncMain执行到第一个任务的时候,又要等第一个任务执行完才能往下执行第二个和第三个任务。这样syncMain方法和第一个任务就开始了互相等待,形成了死锁。

6. 主线程异步:主线程上顺序执行

其他用法:

GCD栅栏:dispatch_barrier_async,可以分组执行。

     GCD延时执行:dispatch_after。

GCD一次执行:dispatch_once能保证某段代码在程序运行过程中只被执行1次。常用于单例。

    GCD快速迭代:dispatch_apply。常用于遍历

    GCD队列组:异步,并发。dispatch_group_create创建一个队列组,dispatch_group_notify监听任务是否完成。

 

NSOPeration:

基于底层的GCD,比GCD多了一些更简单使用的功能,使用更加面向对象,OC语言的,自动管理内存。经常使用。

     NSOperation实现多线程步骤:创建任务:先将需要执行的操作封装到NSOperation对象中。创建队列:创建NSOperationQueue。将任务加入到队列中:将NSOperation对象添加到NSOperationQueue中。

创建方式:

    1.NSInvocationOperation对象并关联方法,之后start。(在主线程下执行,没有开启新线程。如果加入队列,就会开启新的线程,在子线程下执行)

    2.NSBlockOperation创建对象,创建block中的任务是在主线程执行(加入队列,会开启新的线程,在自线程下执行)。

   addExecutionBlock加入的任务是在子线程中执行。

   3.继承自NSOperation的子类。然后重写它的main方法,

队列NSOperationQueue

主队列、其他队列(并发和串行)。

最大并发数。maxConcurrentOperationCount-1,代表多线程)

NSOperation的其他操作:

1. - (void)cancelAllOperations    //取消队列NSOperationQueue的所有操作。

     2. - (void)cancel                //取消NSOperation的某个操作,NSOperation对象方法  

     3.[queue setSuspended:YES];      //使队列暂停或继续

 4. - (BOOL)isSuspended          //判断队列是否暂停

     5.  [operation2 addDependency:operation1];   //操作依赖,2依赖1

三.线程安全问题:

    1.互斥锁:当新线程访问时,如果发现其他线程正在执行锁定的代码,新线程就会进入休眠。

    2.自旋锁:加了自旋锁,当新线程访问代码时,如果发现有其他线程正在锁定代码,新线程会用死循环的方式,一直等待锁定的代码执行完成。相当于不停尝试执行代码,比较消耗性能。

    

    nonatomic 非原子属性,同一时间可以有很多线程读和写。非线程安全,不过效率更高。

atomic 原子属性(线程安全),保证同一时间只有一个线程能够写入(但是同一个时间多个线程都可以取值),atomic 本身就有一把锁(自旋锁),线程安全,需要消耗大量的资源。

 

四.Block的一些问题:

1.IOS项目中使用的好处和缺点?

    好处:

    使用线程可以把占据时间长的程序中的任务放到后台去处理

    用户界面可以更加吸引人,这样比如用户点击了一个按钮去触发某些事件的处理,可以弹出一个进度条来显示处理的进度

    程序的运行效率可能提高

    在一些等待的任务实现上如用户输入、文件读写和网络收发数据等,线程就比较有用了。

缺点:

如果有大量的线程,会影响性能,因为操作系统需要在它们之间切换。

    更多的线程需要更多的内存空间。

    线程的中止需要考虑其对程序运行的影响。

2.一次执行是怎么做到的?如果让你来实现dispatch_once,你会怎么做?

    1.线程A执行Block时,任何其它线程都需要等待。

    2.线程A执行完Block应该立即标记任务完成状态,然后遍历信号量链来唤醒所有等待线程。

    3.线程A遍历信号量链来signal时,任何其他新进入函数的线程都应该直接返回而无需等待。

    4.线程A遍历信号量链来signal时,若有其它等待线程B仍在更新或试图更新信号量链,应该保证此线程B能正确完成其任务:a.直接返回b.等待在信号量上并很快又被唤醒。

    5.线程B构造信号量时,应该考虑线程A随时可能改变状态(“等待”、“完成”、“遍历信号量链”)。

    6.线程B构造信号量时,应该考虑到另一个线程C也可能正在更新或试图更新信号量链,应该保证B、C都能正常完成其任务:a.增加链节并等待在信号量上b.发现线程A已经标记“完成”然后直接销毁信号量并退出函数。

3. NSOperation与GCD的区别?

    GCD是将任务(block)添加到队列(串行、并行、全局、主队列),并且以同步/异步的方式执行任务的函数。GCD提供了一些NSOperation不具备的功能:一次性执行,延迟执行,调度组,GCD 是严格的队列,先进先出FIFO;     

    NSOperation是将操作(异步的任务)添加到队列(并发队列),就会执行指定的函数。NSOperation提供的方便操作:1.最大并发数。2.队列的暂停和继续。3.取消所有的操作。4.指定操作之间的依赖关系依赖关系,可以让异步任务同步执行。5.将KVO用于NSOperation中,监听一个operation是否完成。6.能够设置NSOperation的优先级,能够使同一个并行队列中的任务区分先后地执行。7.对NSOperation进行继承,在这之上添加成员变量与成员方法,提高整个代码的复用度。

2016-11-23 22:42:41 kuangdacaikuang 阅读数 825

0.线程状态

这里写图片描述

新建 --> 就绪(在可调度线程池中,等待被CPU的调度执行) --> 运行
运行 --> CPU切换到其他的线程 --> 就绪
运行 --> 调用的sleep方法 --> 阻塞 --> sleep的时间到了 --> 就绪
运行 --> 任务执行完成 --> 死亡
运行 --> exit --> 死亡

1.新建

内存中创建了一个线程对象

NSThread *thread = [[NSThread alloc] initWithTarget:self selector:@selector(threadDemo) object:nil];

2.就绪

将线程放进可调度线程池,等待被CPU调度

[thread start];

3.运行

CPU负责调度”可调度线程池”中的处于”就绪状态”的线程
线程执行结束之前,状态可能会在”就绪”和”运行”之间来回的切换
“就绪”和”运行”之间的状态切换由CPU来完成,程序员无法干涉

4.阻塞

正在运行的线程,当满足某个条件时,可以用休眠或者锁来阻塞线程的执行

1.sleepForTimeInterval:休眠指定时长
[NSThread sleepForTimeInterval:1.0];

2.sleepUntilDate:休眠到指定日期
[NSThread sleepUntilDate:[NSDate dateWithTimeIntervalSinceNow:1.0]];

3.互斥锁
@synchronized(self)

5.死亡

  • 正常死亡:线程执行结束
  • 非正常死亡:程序突然崩溃/当满足某个条件后,在线程内部强制线程退出,调用exit方法

代码演练

- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
    // 新建状态
    NSThread *thread = [[NSThread alloc] initWithTarget:self selector:@selector(threadDemo) object:nil];
    // 就绪状态 : 将线程放进"可调度线程池",等待被CPU调度.
    [thread start];

    // 主线程中的危险操作,不能在主线程中调用该方法.会使主线程退出
//    [NSThread exit];
}

- (void)threadDemo
{
    for (int i = 0; i < 6; i++) {

        NSLog(@"%d",i);

        //1. 当前线程,每循环一次,就休眠一秒
        [NSThread sleepForTimeInterval:1.0];

        //2. 满足某一条件再次休眠一秒
        if (2==i) {
            NSLog(@"我还想再睡一秒");
            // 休眠时间为从现在开始计时多少秒以后
            [NSThread sleepUntilDate:[NSDate dateWithTimeIntervalSinceNow:1.0]];
        }

        //3. 满足某一条件线程死亡
        if (4==i) {
            NSLog(@"线程死亡");

            // 在调用exit方法之前一定要注意释放之前由C语言框架创建的对象.
            CGMutablePathRef path = CGPathCreateMutable();
            CGPathRelease(path);

            // 线程死亡
            [NSThread exit];

            // 当线程死亡之后,以后的代码都不会被执行
            NSLog(@"线程已经死亡");
        }
    }
    NSLog(@"循环结束");
}

关于exit的结论

  • 使当前线程退出.
  • 不能在主线程中调用该方法.会使主线程退出.
  • 当当前线程死亡之后,这个线程中的代码都不会被执行.
  • 在调用此方法之前一定要注意释放之前由C语言框架创建的对象.

iOS多线程

阅读数 209

iOS多线程总结

阅读数 408

iOS之多线程精髓

阅读数 175

ios多线程详解

阅读数 310

没有更多推荐了,返回首页