多线程的初步理解

来源:互联网 发布:软件体系结构评估文档 编辑:程序博客网 时间:2024/05/17 02:12
1、多线程的状态:
     当线程创建时,只是在内存中创建了;只有在线程进入  就绪状态  的时候,线程才会加入  可调度线程池   ;这样CPU便可以调用线程;

2、多线程使用的几种方法:

(1)pthread  可移植多线程使用:
      //创建pthread
   pthread_tPID;

   NSString*str =@"str";
   //char *str = "char * ssss";
    //参数1pthread_t线程的标示
   //参数2pthread_attr_t线程的属性
   //参数3void *   (*)    (void *)函数签名
   //     返回值   函数名 参数类型
   //void *相当于oc中的id类型,可以指向任意;
   //    //参数4:给函数指定的参数
   //__bridge标示非oc对象也需要被内存管理释放,在ARC机制下,编译器不对基础变量做内存管理;
  int result =pthread_create(&PID,NULL,task2, (__bridge void*)(str));

(2)NSThread方法:创建的线程有.name属性,可以给线程一个唯一的名字,方便自己调试;
     
优先级属性: .threadPriority;赋值的范围是0.0-1.0。1.0是最高的优先级,默认的是0.5;但是优先级高并不意味着会被先执行;

      在子线程需要主线程更新UI界面时,需要调用,方法就会在主线程执行;
[self performSelectorOnMainThread:@selector(updateUI:)withObject:image waitUntilDone:NO];

NSThread的创建的三种方法:
      1、需要手动开启线程
    NSThread*thread = [[NSThread alloc]initWithTarget:self     selector:@selector(task)object:nil];
  //开启线程  [threadstart];

    2、自动开启线程,一创建就可以被CPU调用;
    [NSThread detachNewThreadSelector:@selector(task)toTarget:selfwithObject:nil];

    3、影式的创建线程
    [self performSelectorInBackground:@selector(task)withObject:nil];

(3)GCD的创建方法:
    dispatch_async  和   dispatch_sync创建同步或者异步的多线程;参数一:是一个消息队列;参数二:是需要执行的任务,是一个无参数无返回值的Block;

    在GCD中,如果需要做UI跟新,或者需要回到主线程;只是需要获取  主队列  (主队列是一个串行的队列,会用不执行);将任务添加进去就可以了:
     dispatch_async(dispatch_get_main_queue(), ^{
      code;
 });

    在GCD有很多个任务的时候,并不是有多少个任务就会开多少的子线程,子线程上的任务执行完成后并不会马上销毁,会被重新使用,所以用GCD创建多线程异步执行的时候并不知道有多少子线程被创建;

(4)NSOperation方法:这是一个抽象类,不能实例化;此方法创建多线程时,都是通过子类,或者自定义子类实现(需要实现对应的方法);这个类其实是对GCD的一个封装,但其实NSOperation出现的时间比GCD 早;
    NSOperation创建多线程时,首先需要一个  操作队列  ,即NSOperationQueue。
    NSOperationQueue里面有两个属性是特有的:suspended、maxConcurrentOperationCount;前者是将队列挂起操作(即暂停操作队列的操作,但是对当前正在执行的操作不受影响);后者是对最大并发数的限制,即在同一时刻最多能有几个操作在执行;
   还有一个方法 cancelAllOperations;这是取消队列中剩下的操作,会将队列清空;

    创建操作队列后,可以直接使用Block添加操作至队列:
     [self.opQueue addOperationWithBlock:^{
        <#所需要执行的操作#>
    }]
    也可以先创建NSOperation的子类NSInvocationOperation、NSBlockOperation的一个操作;然后添加至创建的操作队列,如果不添加操作队列,NSOperation有一个start属性,这样就不会开启子线程,会在当前线程执行(相当就是在主线程);
     
    依赖关系:操作之间可以设置执行的顺序,是通过依赖关系;而且可以  跨队列  设置依赖关系;比如:op1操作一栏op2,op2依赖于op3;它们的执行顺序就是:op3、op2、op1;
     [op1addDependency:op2];
    [op2 addDependency:op3];
     
    与主线程的通信:就是获取main队列,然后添加操作:
     [[NSOperationQueue mainQueue]addOperation:op3];
     

    注意事项:
    1、NSOperation创建的操作队列,是异步多线程执行的,相当于GCD中的并发队列;它没有同步执行;
    2、一般在使用的时候,都会创建一个全局的操作队列,方便于管理;



3、多线程之间的数据共享(同步锁和自旋锁)
   当多个线程同时访问数据并且更该时,很容易发生错误;这时就需要给数据加上锁,在同一时间只能有一个线程访问;
   @synchronized(self.tickets){
           if(self.tickets>0){
               
self.tickets=self.tickets- 1;
               
NSLog(@"%@余票%d",[NSThreadcurrentThread],self.tickets);
               
continue;
            }
        }

   @synchronized是同步的意思;线程必须要按顺序执行;这是同步锁;

   自旋锁:是系统帮我们添加的。在我们创建属性的时候有nonatomic和atomic;前者是非原子属性,后者是原子属性,它的内部有一把自旋锁;但是锁的只是setter方法;并没有对getter方法上锁;这可能在判断的时候会出错;

   自旋锁会一直判断能不能访问;相当于一个死循环;会占用内存,所以一般不推荐使用自旋锁;

4、消息分为两种:输入源和定时源;

  当消息需要加入消息循环时,需要设置对应的Modle,否则会没有作用;

  主线程的消息循环值默认开启的,子线的时关闭的,需要手动开启;

  子线程开启消息循环有三种方法:
     1、run(开启便不能停止);
     2、runUntilDate运行到一个时间点(局限性,每个手机的性能不一样);
     3、苹果官方推荐的方法:通过BOOL变量控制;
NSDate是返回一个比较遥远的未来时间;
     BOOL shouldKeepRunning = YES;
     NSRunLoop *theRL = [NSRunLoop currentRunLoop];
  while (shouldKeepRunning&& [theRL runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]]);
    
5、GCD队列:串行队列、并发队列、主队列(一个特殊串行队列);
   串行队列,同步执行,不开新的线程,在当前线程下执行,任务是有序执行的;
    串行队列,异步执行,开一个新的线程,任务是有序执行的;

     并行队列,同步执行不开线程,在当前线程下执行任务顺序执行等于串行队列,同步执行;
     并行队列,异步执行,开多个线程,任务无序执行,效率最大;

     主队列,异步执行在主线线程执行任务顺序执行;
     主队列,同步执行死锁(程序处于互相等待的状态)

   GCD中有一个系统定义的全局并发队列;dispatch_get_global_queue(0,0);第一个参数时优先级;是为了兼容性考虑赋值0,因为IOS8之前和之后的定义有不用;第二个参数,没有意义,方便给未来使用;

   GCD中有一个队列组:
   //创建组
   dispatch_group_tgroup =dispatch_group_create();
   //开启异步任务,将任务添加至组任务。参数1:参数2:队列参数3:任务
    dispatch_group_async(group, dispatch_get_global_queue(0, 0), ^{
    NSLog(@"LOL1.zip %@",[NSThreadcurrentThread]);
});

   //当组任务执行完成后,便可以执行其他的任务;
   dispatch_group_notify(group,dispatch_get_main_queue(), ^{
   NSLog(@"下载完成%@",[NSThreadcurrentThread]);
});

6、执行唯一一次:dispatch_once,block里面的代码无论调用多少次,都只会执行一次;有线程安全;
    static dispatch_once_tonceToken;
    dispatch_once(&onceToken, ^{
        <#code#>
    });
   可以使用这种方法实现单例模式;












0 0