多线程之GCD(2)

GCD第二节 GCD的一个方法的使用


1.线程的通信



1dispatch_async( dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0), ^{// 执⾏耗时的异步操作...

  dispatch_async(dispatch_get_main_queue(), ^{

            // 回到主线程,执⾏UI刷新操作

      });

  });  


(2)[self.imageView performSelectorOnMainThread:@selector(setImage:) withObject:image waitUntilDone:NO];



2.延迟提交 dispatch_after


1)调用NSObject的方法


         [self performSelector:@selector(run) withObject:nil afterDelay:2.0];



- (void)test1{

    NSLog(@"当前线程---%@",[NSThreadcurrentThread]);

    [selfperformSelector:@selector(runDtate)withObject:nilafterDelay:3.0];

    /*

     2016-03-08 14:00:36.071 GCDDemo3[7710:132766] 当前线程---<NSThread: 0x7fc700702160>{number = 1, name = main}

     2016-03-08 14:00:39.073 GCDDemo3[7710:132766] 延迟3---<NSThread: 0x7fc700702160>{number = 1, name = main}

     */

}

- (void)runDtate{

    NSLog(@"延迟3---%@",[NSThreadcurrentThread]);

}


- (void)test2{

    dispatch_queue_t queue =dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0);

    dispatch_async(queue, ^{

        NSLog(@"全局线程-%@",[NSThreadcurrentThread]);

       //方法放到异步函数中不会执行,放到同步函数可以执行

        [selfperformSelector:@selector(runDtate)withObject:nilafterDelay:3.0];

    });

}

- (void)test3{

    dispatch_queue_t queue =dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0);

    dispatch_sync(queue, ^{

        NSLog(@"全局线程-%@",[NSThreadcurrentThread]);

       //方法放到异步函数中不会执行,放到同步函数可以执行

        [selfperformSelector:@selector(runDtate)withObject:nilafterDelay:3.0];

    });

    /*

     2016-03-08 14:07:46.468 GCDDemo3[7906:137075] 全局线程-<NSThread: 0x7f912b406510>{number = 1, name = main}

     2016-03-08 14:07:49.470 GCDDemo3[7906:137075] 延迟3---<NSThread: 0x7f912b406510>{number = 1, name = main}

     */

}



2)使用GCD函数



dispatch_after函数并不是在指定时间后执行处理,而只是在指定时间追加处理到Dispatch Queue 此源代码与在固定描述后,用dispatch_async函数追加到 Block到Main的相同。 这个方法不能准确的计算延迟时间,但是可以用于大概的时间延迟处理。


 dispatch_after(<#dispatch_time_t when#>, <#dispatch_queue_t queue#>, <#^(void)block#>)

  第一个time的解析:

 dispatch_time_t time = dispatch_time(<#dispatch_time_t when#>, <#int64_t delta#>)

    <#dispatch_time_t when#> 第一个参数一般是DISPATCH_TIME_NOW 表示从现在开始。

   第二个参数是真正的延时的具体时间。

    这里的delta是表示纳秒。

    #define NSEC_PER_SEC 1000000000ull  每秒有多少纳秒

    #define NSEC_PER_MSEC 1000000ull    每秒有多少毫秒

    #define USEC_PER_SEC 1000000ull

    #define NSEC_PER_USEC 1000ull  每毫秒有多少纳秒。

    NSEC:纳秒。USEC:微妙 SEC: PER:每

  DISPATCH_TIME_NOW  (int64_t)(5*NSEC_PER_SEC))从现在开始,往后数5秒。


   第二个参数:

     是指定要追加处理的Dispatch Queue

   第三个参数:

  指定要执行处理的block





- (void)test4{

    dispatch_queue_t queue = queue =dispatch_queue_create("myTest1",DISPATCH_QUEUE_CONCURRENT);

    NSLog(@"创建完队列");

    dispatch_async(queue, ^{

        [NSThreadsleepForTimeInterval:10];

        NSLog(@"睡眠10s后执行");

    });

    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(5*NSEC_PER_SEC)), queue, ^{

        NSLog(@"After 5");

    });

}

/*

 2016-03-14 14:37:00.076 GCDDemo3[10866:174778] 创建完队列

 2016-03-14 14:37:10.082 GCDDemo3[10866:174956] 睡眠10s后执行

 2016-03-14 14:37:10.083 GCDDemo3[10866:174956] After 5

 */

- (void)test5{

    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(5*NSEC_PER_SEC)),dispatch_get_main_queue(), ^{

        NSLog(@"After 5");

        [self delay5];

    });

}

- (void)delay5{

    NSLog(@"放入到after中的方法");

}

/*

 2016-03-14 14:34:30.225 GCDDemo3[10782:173039] After 5

 2016-03-14 14:34:30.226 GCDDemo3[10782:173039] 放入到after中的方法

 */




3. dispatch_once_t 


dispatch_once函数是保证在应用程序执行中只执行一次指定处理的API。防止多个线程同时争夺同一个资源,使用dispatch_once能保证在多线程的环境下也能保证百分百的安全。


单利


+(Manager *)sharManagerData{

    static Manager * shareManager =nil;

    static dispatch_once_t onceToken;

    dispatch_once(&onceToken,^{

        shareManager = [[selfalloc]init];

    });

    return shareManager;

}



这里想延伸处理个问题

(1)单利

      全局变量  单利可以被用作全局变量来使用

    (1.1)我们可以使用AppDelegate来创建一个全局的变量(AppDelegate本身就是一个单利类)。

     也可以自己单独创建一个单列类用来创建全局的变量

     (1.2)使用exten创建一个全局的变量


 iOS中好几个类都是采用了单例模式,比如NSApplication NSFontManager,   NSDocumentController,NSHelpManager, NSNull,NSProcessInfo, NSScriptExecutionContext,   NSUserDefaults.

   

(2)静态变量

        iOS使用静态变量

         static写在interface外面编译是没有错误的,但是编译器会报警告,这么说这样的写法是不被编辑器认可的。

(2.1)两个类之间传递值

     [NextViewControllerinstanceProjectid]  可以使用这种方式对静态变量projectID进行访问,无须创建实例。

这样做可以防止,多个类进入到同一个类时,会导致某个变量被其他的类修改。


//第二个类


#import <UIKit/UIKit.h>

@interface NextViewController :UIViewController

+(int)instanceProjectid;

@end


#import "NextViewController.h"

@interfaceNextViewController ()

@property (strong,nonatomic)IBOutletUITextField *textfield;

@end


//OC使用静态变量 1

static int projectID;

@implementation NextViewController

- (void)viewDidLoad {

    [superviewDidLoad];

}


-(void)touchesEnded:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{

    //OC使用静态变量 2

    projectID = [self.textfield.textintValue];

    [selfdismissViewControllerAnimated:YEScompletion:nil];

}

  //OC使用静态变量 3

+(int)instanceProjectid{

    returnprojectID;

}

@end


//第一个类

#import "ViewController.h"

#import "NextViewController.h"

#import "Manager.h"

@interfaceViewController ()

@property (nonatomic,strong)UIButton * button;

@property (nonatomic,strong)UILabel * label;

@end

@implementation ViewController

-(void)viewWillAppear:(BOOL)animated{

    [super viewWillAppear:animated];

      //OC使用静态变量 4

     self.label.text =[NSStringstringWithFormat:@"%d",[NextViewControllerinstanceProjectid]];

    

}

- (void)viewDidLoad {

    [superviewDidLoad];

    self.label = [[UILabelalloc]initWithFrame:CGRectMake(100,200,200, 30)];

    self.label.backgroundColor = [UIColorgreenColor];

    [self.viewaddSubview:self.label];

    

    self.button = [[UIButtonalloc]initWithFrame:CGRectMake(100,100,100, 40)];

    self.button.backgroundColor = [UIColoryellowColor];

    [self.buttonsetTitle:@"next"forState:UIControlStateNormal];

    [self.buttonaddTarget:selfaction:@selector(buttonclick:)forControlEvents:UIControlEventTouchUpInside];

    [self.viewaddSubview:self.button];

}


- (void)buttonclick:(UIButton *)btn{

    NextViewController * next = [[NextViewControlleralloc]initWithNibName:@"NextViewController"bundle:nil];

    [selfpresentViewController:nextanimated:YEScompletion:nil];

}

@end



3.dispatch_suspend/dispatch_resume挂起和恢复队列


当在queue中追加了一些处理过程,有时希望不执行已经追加的处理,可以使用这两个方法

但是dispatch_suspend函数对于已经执行的处理没有影响。挂起后,追加到queue中的处理,但是尚未执行的处理,在此之后停止执行,而恢复后则继续执行



- (void)test6{

   //DISPATCH_QUEUE_SERIAL创建一个串行队列

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

    dispatch_async(queue, ^{

        [NSThreadsleepForTimeInterval:5];

        NSLog(@"sleep5");

    });


    NSLog(@"挂起队列");

    dispatch_suspend(queue);

    NSLog(@"睡眠10");

    [NSThreadsleepForTimeInterval:10];

    dispatch_async(queue, ^{

        NSLog(@"第二个queue");

    });

    NSLog(@"恢复队列");

    dispatch_resume(queue);

 

}

/*

 队列挂起后block还在运行正常的输出,所以,dispatch_suspend并不会立即暂停正在运行的block

 注意这里如果注释了回复队列的代码,程序是会崩溃的

 2016-03-16 21:32:08.860 GCDDemo3[1272:17323] 挂起队列

 2016-03-16 21:32:08.861 GCDDemo3[1272:17323] 睡眠10

 2016-03-16 21:32:13.866 GCDDemo3[1272:17371] sleep5

 2016-03-16 21:32:18.863 GCDDemo3[1272:17323] 恢复队列

 2016-03-16 21:32:18.864 GCDDemo3[1272:17371] 第二个queue


 */



4.dispatch_apply


dispatch_apply函数是dispatch_sync函数和Dispatch Queue的关联API。该函数按照指定的次数,将指定的block追加到指定的Dispatch Queue中,并等待全部处理执行结束


dispatch_apply(<#size_t iterations#>, <#dispatch_queue_t queue#>, <#^(size_t)block#>)

第一个参数:重复的次数

第二个参数:追加的对象的Dispatch Queue

第三个参数:追加的处理



-(void)test7{

    dispatch_queue_t queue =dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0);

    dispatch_apply(10, queue, ^(size_t index) {

        NSLog(@"%zu",index);

    });

    NSLog(@"done");

}

/*

 因为是在Global Queue中执行的,所以各个处理的执行时间不定,但是输出结果中最后出现的“done”必定在最后位置上。因为dispatch_apply函数会等待全部的处理执行结束。

 

 2016-03-16 22:36:18.013 GCDDemo3[1388:15648] 1

 2016-03-16 22:36:18.013 GCDDemo3[1388:15647] 2

 2016-03-16 22:36:18.013 GCDDemo3[1388:15588] 0

 2016-03-16 22:36:18.013 GCDDemo3[1388:15651] 3

 2016-03-16 22:36:18.014 GCDDemo3[1388:15588] 6

 2016-03-16 22:36:18.014 GCDDemo3[1388:15648] 4

 2016-03-16 22:36:18.014 GCDDemo3[1388:15647] 5

 2016-03-16 22:36:18.014 GCDDemo3[1388:15651] 7

 2016-03-16 22:36:18.014 GCDDemo3[1388:15588] 8

 2016-03-16 22:36:18.014 GCDDemo3[1388:15648] 9

 2016-03-16 22:36:18.015 GCDDemo3[1388:15588] done

 */

因为dispatch_apply函数和dispatch_sync一样,会等待处理执行的结果。所以建议把在dispatch_async中执行非同步的dispatch_apply函数。

- (void)test8{

    NSArray * array =@[@"第一个",@"第二个",@"第三个",@"第四个",@"第五个"];

    dispatch_queue_t queue =dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0);

    dispatch_async(queue, ^{

        dispatch_apply([arraycount], queue, ^(size_t index) {

            NSLog(@"%zu,%@",index,[arrayobjectAtIndex:index]);

        });

        dispatch_async(dispatch_get_main_queue(), ^{

            NSLog(@"回到主线程");

        });

    });

    /*

    这个可以把前面写的下载多张图片的代码整合一下了。执行的顺序是不确定的

     2016-03-16 22:54:17.405 GCDDemo3[1868:23796] 2,第三个

     2016-03-16 22:54:17.405 GCDDemo3[1868:23797] 0,第一个

     2016-03-16 22:54:17.405 GCDDemo3[1868:23803] 3,第四个

     2016-03-16 22:54:17.405 GCDDemo3[1868:23795] 1,第二个

     2016-03-16 22:54:17.406 GCDDemo3[1868:23796] 4,第五个

     2016-03-16 22:54:17.413 GCDDemo3[1868:23776] 回到主线程

     */

}



5.dispatch_group 


使用的例子:

例如,我们遇到一个界面多个请求,但是必须等到这多个请求全部结束的时候,才能展示界面的数据,这是我们就可以使用dispatch_group来帮住我们解决这个问题;


在追加到dispatch_queue中的多个处理全部结束后想执行结束处理,这个时候我们就可以有两种处理方法

(1)串行队列,把向执行都放到串行队列中,在最后追加结束后处理即可

(2)使用并发队列,并且同时多个,这种情况下就可以使用Dispatch Group 

dispatch_group的使用步骤

(1)创建dispatch_group_t  创建一个group  使用的是dispatch_group_create()

(2)添加任务

(3)结束任务

添加任务:

1.自己创建队列 dispatch_group_async

无法直接使用队列变量

2.参考文章  http://blog.csdn.net/majiakun1/article/details/38642833


AFHTTPRequestOperationManager *manager = [AFHTTPRequestOperationManager manager];

//Enter group

dispatch_group_enter(group);

[manager GET:@"http://www.baidu.com" parameters:nil success:^(AFHTTPRequestOperation *operation, id responseObject) {

    //Deal with result...

    //Leave group

    dispatch_group_leave(group);

}    failure:^(AFHTTPRequestOperation *operation, NSError *error) {

    //Deal with error...

    //Leave group

    dispatch_group_leave(group);

}];

//More request...

使用dispatch_group_enterdispatch_group_leave就可以方便的将一系列网络请求打包起来~


3,添加结束任务

1)在当前线程阻塞的同步等待:dispatch_group_wait;等待全部执行结束

第二个参数:超时时间

#define DISPATCH_TIME_NOW (0ull)

#define DISPATCH_TIME_FOREVER (~0ull)永久等待

 dispatch_group_wait(<#dispatch_group_t group#>, <#dispatch_time_t timeout#>)




- (void)test10{

    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, ^{

        NSLog(@"test10----1");

    });

    dispatch_group_async(group, queue, ^{

        NSLog(@"test10----2");

    });

    dispatch_group_async(group, queue, ^{

        NSLog(@"test10----3");

    });

    dispatch_group_async(group, queue, ^{

        NSLog(@"test10----4");

    });

    dispatch_group_async(group, queue, ^{

        NSLog(@"test10----5");

    });

    dispatch_group_async(group, queue, ^{

        NSLog(@"test10----6");

    });

    dispatch_group_async(group, queue, ^{

        NSLog(@"test10----7");

    });

    dispatch_group_async(group, queue, ^{

        NSLog(@"test10----8");

    });

    dispatch_group_async(group, queue, ^{

        NSLog(@"test10----9");

    });

    dispatch_group_async(group, queue, ^{

        NSLog(@"test10----10");

    });

    

    dispatch_time_t time =dispatch_time(DISPATCH_TIME_NOW,1 * NSEC_PER_SEC);

  long result = dispatch_group_wait(group, time);

    if(result == 0){

        NSLog(@"done");

    }else{

        NSLog(@"还没有执行完成");

    } 

}

/*

 2016-03-18 13:32:41.880 GCDDemo3[16405:121386] test10----1

 2016-03-18 13:32:41.881 GCDDemo3[16405:121391] test10----5

 2016-03-18 13:32:41.880 GCDDemo3[16405:121385] test10----2

 2016-03-18 13:32:41.880 GCDDemo3[16405:121387] test10----3

 2016-03-18 13:32:41.880 GCDDemo3[16405:121390] test10----4

 2016-03-18 13:32:41.881 GCDDemo3[16405:121392] test10----6

 2016-03-18 13:32:41.881 GCDDemo3[16405:121386] test10----7

 2016-03-18 13:32:41.881 GCDDemo3[16405:121391] test10----8

 2016-03-18 13:32:41.881 GCDDemo3[16405:121387] test10----9

 2016-03-18 13:32:41.881 GCDDemo3[16405:121385] test10----10

 2016-03-18 13:32:41.882 GCDDemo3[16405:121206] done

 

 */


dispatch_group_wait函数的返回值不是0,就意味这虽然经历了指定的时间,但Dispatch Group的某个处理还在执行中。如果返回值是0,那么全部执行完成了。当等待时间为DISPATCH_TIME_FOREVER 由于属于Dispatch Group的处理必定全部执行完成了。因此返回值会恒为0;



2)添加一个异步执行的任务作为结束任务后:dispatch_group_notify


- (void)test9{

    dispatch_queue_t queue =dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0);


创建一个group

    dispatch_group_t group =dispatch_group_create();

添加任务

    dispatch_group_async(group, queue, ^{

        NSLog(@"group1");

    });

    dispatch_group_async(group, queue, ^{

        NSLog(@"group2");

    });

    dispatch_group_async(group, queue, ^{

        NSLog(@"group3");

    });

    dispatch_group_async(group, queue, ^{

        NSLog(@"group4");

    });

    dispatch_group_async(group, queue, ^{

        NSLog(@"group5");

    });

    dispatch_group_notify(group,dispatch_get_main_queue(), ^{

        NSLog(@"done");

    });

    /*

创建一个全局的队列,创建一个group, 把队列加入到group中,在group的异步函数中执行;


     016-03-17 22:39:59.354 GCDDemo3[2271:23666] group2

     2016-03-17 22:39:59.354 GCDDemo3[2271:23667] group1

     2016-03-17 22:39:59.354 GCDDemo3[2271:23669] group4

     2016-03-17 22:39:59.354 GCDDemo3[2271:23668] group3

     2016-03-17 22:39:59.354 GCDDemo3[2271:23670] group5

     2016-03-17 22:39:59.359 GCDDemo3[2271:23626] done

     */ 

}



6. dispatch_sync 同步


同步很容易就引起死锁,这是不可避免的。在主线程中使用同步方法,必然会引起死锁

dispatch_apply和dispatch_sync同理,也很容易引起死锁;


7.dispatch_barrier_async


dispatch_barrier_async的作用就是在某个队列中插入一个block,当目前正在执行的block运行完毕后,阻塞这个block后面添加的block,只运行这个block直到完成,然后再继续后续的任务。


使用concurrent  dispatch queue  和 dispatch barrier async 可以实现高效的数据库访问和文件访问。

这个我还没有遇到真是的案例。有待后续学习。



-(void)test11{

    dispatch_queue_t queue =dispatch_queue_create("myTest11",DISPATCH_QUEUE_CONCURRENT);

    dispatch_async(queue, ^{

        NSLog(@"queue1");

    });

    dispatch_async(queue, ^{

        NSLog(@"queue2");

    });

    dispatch_barrier_async(queue, ^{

        NSLog(@"插入的操作");

    });

    dispatch_barrier_async(queue, ^{

        NSLog(@"queue3");

    });

}

/*

 dispatch_barrier_async这个方法的提出是为了解决一个问题,那就是在某些任务完成后就执行指定的任务,该方法会阻塞队列中未执行的任务。

 

 2016-03-21 22:28:45.342 GCDDemo3[894:11775] queue2

 2016-03-21 22:28:45.342 GCDDemo3[894:11774] queue1

 2016-03-21 22:28:45.342 GCDDemo3[894:11774] 插入的操作

 2016-03-21 22:28:45.342 GCDDemo3[894:11774] queue3

 */


dispatch_barrier_async/dispatch_barrier_sync只在自己创建的并发队列中有效,在全局并发队列、串行队列中效果和dispatch_async/dispatch_sync相同



8.dispatch_set_context


这部分内没有实战的操作,在这查找了网上的一些资料

dispatch_set_context可以为队列添加上下文数据,但是因为GCDC语言接口形式的,所以其context参数类型是“void *”。也就是说,我们创建context时有如下几种选择:

(1)用C语言的malloc创建context数据。

(2)用C++new创建类对象。

(3)用Objective-C的对象,但是要用__bridge等关键字转为Core Foundation对象。

以上所有创建context的方法都有一个必须的要求,就是都要释放内存!,无论是用freedelete还是CFCFRelease,我们都要确保在队列不用的时候,释放context的内存,否则就会造成内存泄露。

所以,使用dispatch_set_context的时候,最好结合dispatch_set_finalizer_f使用,为队列设置析构函数,在这个函数里面释放内存,大致如下:

void cleanStaff(void *context) {

    //释放context的内存!

    //CFRelease(context);

    //free(context);

    //delete context;

}

...

//在队列创建后,设置其析构函数

dispatch_set_finalizer_f(queue, cleanStaff);


9.Dispatch Semaphore


当并行执行的处理 更新数据时,会产生数据不一致的情况,这个问题的解决可以使用Serial Dispatch QueueDispatch_barrier_async 这个函数。



-(void)test12{

    dispatch_queue_t queue =dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0);

    NSMutableArray * array = [[NSMutableArrayalloc]init];

    for(int i =0; i<10000;i++){

        dispatch_async(queue, ^{

            [array addObject:[NSNumbernumberWithInt:i]];

        });

    }

}

/*

 malloc: *** error for object 0x7ffe60506550: pointer being freed was not allocated

 *** set a breakpoint in malloc_error_break to debug

 由于内存导致的应用程序结束。我们可以修改为test13的版本

 */

-(void)test13{

    dispatch_queue_t queue =dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0);

    dispatch_semaphore_t semaphore =dispatch_semaphore_create(1);

     NSMutableArray * array = [[NSMutableArrayalloc]init];

    for(int i =0;i<10000;i++){

        dispatch_async(queue, ^{

            dispatch_semaphore_wait(semaphore,DISPATCH_TIME_FOREVER);

            [array addObject:[NSNumbernumberWithInt:i]];

            dispatch_semaphore_signal(semaphore);

        

        });

    }

    NSLog(@"array--%@",array);

    NSLog(@"donw");

}

/*

 2016-03-23 13:59:49.744 GCDDemo3[8278:164595] array--(

 0,

 

 9999

 )

 2016-03-23 13:59:49.877 GCDDemo3[8278:164595] donw

 */



 dispatch_semaphore是持有计数信号,该计数是多线程编程中的计数类型信号。当计数为0时等待,计数为1或大于1时,减去1而不等待。


1)创建

 dispatch_semaphore_t semaphore =dispatch_semaphore_create(1);


2)信号的返回

dispatch_semaphore_wait(semaphore,DISPATCH_TIME_FOREVER);


等待计数值达到大于后等于1,当计数值大于等于1,或者在待机中计数值等于1时对该计数值进行减法并从该函数中返回。返回值为0,可以安全的执行需要进行排他控制的处理,该处理结束的时候通过dispatch_semaphore_signal(semaphore);dispatch_semaphore的计数加1



10.还有一个比较次要点的Dispatch Sources 

http://blog.csdn.net/nogodoss/article/details/31346207

可以看看



你可能感兴趣的:(多线程,通信,异步,gcd)