GCD

作者: liboxiang | 来源:发表于2018-05-17 23:48 被阅读8次

    官网链接:https://developer.apple.com/library/archive/documentation/General/Conceptual/ConcurrencyProgrammingGuide/GCDWorkQueues/GCDWorkQueues.html

    dispatch queues type 队列分类

    • serial 串行队列
    • concurrent 并发队列
    • Main dispatch queue 主队列

    注意点:

    • Private dispatch queues are reference-counted objects. In addition to retaining the queue in your own code, be aware that dispatch sources can also be attached to a queue and also increment its retain count. Thus, you must make sure that all dispatch sources are canceled and all retain calls are balanced with an appropriate release call.
      私有调度队列是引用计数的对象。除了在自己的代码中保留队列之外,请注意,分派源也可以附加到队列中,并增加其保留计数。因此,您必须确保所有派送源都被取消,并且所有保留呼叫均通过适当的释放呼叫进行平衡。
    • 如果您的块创建了多个Objective-C对象,则可能需要将块的部分代码放在@autorelease块中以处理这些对象的内存管理。尽管GCD调度队列拥有自己的自动释放池,但他们无法保证这些池何时耗尽。如果您的应用程序受内存限制,则创建您自己的自动释放池可以让您以更常规的时间间隔为自动释放对象释放内存

    创建队列

    • 创建串行队列
    dispatch_queue_t queue;
    queue = dispatch_queue_create("com.example.MyQueue", NULL);
    

    队列的内存管理

    队列和其他调度对象是引用计数的数据类型创建串行调度队列时,它的初始引用计数为1.您可以使用 dispatch_retaindispatch_release函数根据需要递增和递减引用计数。当队列的引用计数达到零时,系统异步解除分配队列。

    You do not need to retain or release any of the global dispatch queues, including the concurrent dispatch queues or the main dispatch queue. Any attempts to retain or release the queues are ignored.
    您不需要保留或释放任何全局分派队列,包括并发分派队列或主分派队列。任何保留或释放队列的尝试都会被忽略。

    使用队列存储自定义上下文信息

    所有分派对象(包括分派队列)都允许您将自定义上下文数据与对象相关联。要在给定的对象上设置和获取这些数据,可以使用dispatch_set_contextdispatch_get_context函数。系统不会以任何方式使用您的自定义数据,并且由您在适当的时间分配和取消分配数据。

    对于队列,您可以使用上下文数据来存储指向Objective-C对象或其他数据结构的指针,以帮助识别队列或其代码的预期用法。您可以使用队列的终结函数在解除分配之前将队列数据从队列中释放(或解除关联)。

    给串行队列附加终结函数

    void myFinalizerFunction(void *context)
    {
        MyDataContext* theData = (MyDataContext*)context;
        
        // Clean up the contents of the structure
        myCleanUpDataContextFunction(theData);
        
        // Now release the structure itself.
        free(theData);
    }
    
    dispatch_queue_t createMyQueue()
    {
        MyDataContext*  data = (MyDataContext*) malloc(sizeof(MyDataContext));
        myInitializeDataContextFunction(data);
        
        // Create the queue and set the context data.
        dispatch_queue_t serialQueue = dispatch_queue_create("com.example.CriticalTaskQueue", NULL);
        dispatch_set_context(serialQueue, data);
        dispatch_set_finalizer_f(serialQueue, &myFinalizerFunction);
        
        return serialQueue;
    }
    

    添加任务到队列

    • 同步任务dispatch_sync或者dispatch_sync_f
    • 异步任务dispatch_async或者dispatch_async_f

    注意点:
    You should never call the dispatch_sync or dispatch_sync_f function from a task that is executing in the same queue that you are planning to pass to the function. This is particularly important for serial queues, which are guaranteed to deadlock, but should also be avoided for concurrent queues.
    串行队列的任务重给当前队列添加同步任务会造成死锁,并发队列也应该避免类似操作。

    同时执行循环迭代

    for (i = 0; i < count; i++) {
       printf("%u\n",i);
    }
    

    如果在每次迭代期间执行的工作与在所有其他迭代期间执行的工作不同,并且每个后续循环完成的顺序不重要,则可以用对dispatch_applyor dispatch_apply_f函数的调用替换循环。这些函数为每个循环迭代提交指定的块或函数一次。当调度到并发队列时,可以同时执行多个循环迭代。

    重要提示:与常规for循环一样,在所有循环迭代完成之前dispatch_applydispatch_apply_f函数都不会返回。因此,在从队列上下文中执行的代码中调用它们时应该小心。如果您作为参数传递给函数的队列是串行队列,并且与执行当前代码的队列相同,则调用这些函数将使队列死锁。

    因为它们有效地阻止当前线程,所以在从主线程调用这些函数时应该小心,因为它们可以防止事件处理循环及时响应事件。如果您的循环代码需要大量的处理时间,则可能需要从不同的线程调用这些函数。

    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
     
    dispatch_apply(count, queue, ^(size_t i) {
       printf("%u\n",i);
    });
    

    你应该确保你的任务代码在每次迭代中都会做一些合理的工作。与任何您派发到队列的块或函数一样,调度该代码以供执行也会有开销。如果循环的每次迭代只执行少量工作,则调度代码的开销可能会超过将它分派到队列中可能带来的性能好处。如果您在测试过程中发现这是真的,则可以使用跨步增加每次循环迭代期间执行的工作量。通过跨越,您可以将原始循环的多个迭代组合到一个块中,并按比例减少迭代次数。例如,如果您最初执行100次迭代,但决定使用4次跨度,则您现在从每个块执行4次循环迭代,并且您的迭代计数为25

    暂停和恢复队列

    You can prevent a queue from executing block objects temporarily by suspending it. You suspend a dispatch queue using the dispatch_suspend function and resume it using the dispatch_resume function. Calling dispatch_suspend increments the queue’s suspension reference count, and calling dispatch_resume decrements the reference count. While the reference count is greater than zero, the queue remains suspended. Therefore, you must balance all suspend calls with a matching resume call in order to resume processing blocks.
    您可以通过挂起暂时阻止队列暂时执行块对象。您使用该dispatch_suspend功能暂停调度队列并使用该功能恢复它dispatch_resume。调用会dispatch_suspend增加队列的暂停引用计数,并调用dispatch_resume减少引用计数。当引用计数大于零时,队列保持挂起状态。因此,您必须将所有挂起的呼叫与匹配的简历呼叫进行平衡,以便恢复处理块。

    暂停和恢复调用是异步的,仅在执行块之间生效。暂停队列不会导致已经执行的块停止。

    取消任务

    - (void)gcdBlockCancel{
        
        dispatch_queue_t queue = dispatch_queue_create("com.gcdtest.www", DISPATCH_QUEUE_CONCURRENT);
        
        dispatch_block_t block1 = dispatch_block_create(0, ^{
            sleep(5);
            NSLog(@"block1 %@",[NSThread currentThread]);
        });
        
        dispatch_block_t block2 = dispatch_block_create(0, ^{
            NSLog(@"block2 %@",[NSThread currentThread]);
        });
        
        dispatch_block_t block3 = dispatch_block_create(0, ^{
            NSLog(@"block3 %@",[NSThread currentThread]);
        });
        
        dispatch_async(queue, block1);
        dispatch_async(queue, block2);
        dispatch_block_cancel(block3);
    }
    
    2017-07-08 13:59:39.935 beck.wang[2796:284866] block2 <NSThread: 0x6180000758c0>{number = 3, name = (null)}
    2017-07-08 13:59:44.940 beck.wang[2796:284889] block1 <NSThread: 0x618000074f80>{number = 4, name = (null)}
    

    Dispatch groups 调度组

    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    dispatch_group_t group = dispatch_group_create();
    
    // Add a task to the group
    dispatch_group_async(group, queue, ^{
        // Some asynchronous work
    });
    
    // Do some other work while the tasks execute.
    
    // When you cannot make any more forward progress,
    // wait on the group to block the current thread.
    dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
    
    // Release the group when it is no longer needed.
    dispatch_release(group);
    
    /*!
     * @function dispatch_group_enter
     *
     * @abstract
     * Manually indicate a block has entered the group
     *
     * @discussion
     * Calling this function indicates another block has joined the group through
     * a means other than dispatch_group_async(). Calls to this function must be
     * balanced with dispatch_group_leave().
     *
     * @param group
     * The dispatch group to update.
     * The result of passing NULL in this parameter is undefined.
     */
    API_AVAILABLE(macos(10.6), ios(4.0))
    DISPATCH_EXPORT DISPATCH_NONNULL_ALL DISPATCH_NOTHROW
    void
    dispatch_group_enter(dispatch_group_t group);
    
    /*!
     * @function dispatch_group_leave
     *
     * @abstract
     * Manually indicate a block in the group has completed
     *
     * @discussion
     * Calling this function indicates block has completed and left the dispatch
     * group by a means other than dispatch_group_async().
     *
     * @param group
     * The dispatch group to update.
     * The result of passing NULL in this parameter is undefined.
     */
    API_AVAILABLE(macos(10.6), ios(4.0))
    DISPATCH_EXPORT DISPATCH_NONNULL_ALL DISPATCH_NOTHROW
    void
    dispatch_group_leave(dispatch_group_t group);
    
    dispatch_group_enter(group);
        dispatch_group_async(group, self.barrierQueue, ^{
            NSLog(@"start 2");
            sleep(2);
            NSLog(@"2");
            dispatch_group_leave(group);
        });
    
    dispatch_group_enter、dispatch_group_leave配合使用,实际上就是创建了信号量
    

    Dispatch semaphores 调度信号量

    dispatch_semaphore是GCD用来同步的一种方式,与他相关的共有三个函数,分别是
    dispatch_semaphore_create,dispatch_semaphore_signal,dispatch_semaphore_wait。

    注意:下面两行代码得到的信号不相同

    dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
    dispatch_semaphore_t semaphoreNew = dispatch_semaphore_create(0);
    
    信号量用于网络请求的demo
    并发网络请求
    // 创建信号量
        dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
        // 创建全局并行
        dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
        dispatch_group_t group = dispatch_group_create();
        for (int i = 0; i < array.count; i++) {
            NSString *uid = array[I];
            dispatch_group_async(group, queue, ^{
                NSMutableDictionary *workflowParamsDic = [NSMutableDictionary dictionaryWithCapacity:0];
                TYDIC(workflowParamsDic, @"WorkflowInstanceId", uid);
                NSURLSessionDataTask *task = [[TYNetClient sharedClient] httpGetUseCache:YES params:workflowParamsDic module:@"GetConstructionList" showLoading:nil onView:nil completion:^(TYBaseResultModel *result) {
                    TYLog(@"%d",i);
                    dispatch_semaphore_signal(semaphore);
                } error:^(NSError *error) {
                    dispatch_semaphore_signal(semaphore);
                }];
                NSLog(@"Test serial %d %@",i, task);
            });
        }
        
        dispatch_group_notify(group, queue, ^{
            // 三个请求对应三次信号等待
            for (int i = 0; i < array.count; i++) {
                dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
                NSLog(@"semaphore %ld",i);
            }
            NSLog(@"first");
            //在这里 进行请求后的方法,回到主线程
            dispatch_async(dispatch_get_main_queue(), ^{
                
                //更新UI操作
                NSLog(@"finish");
            });
        });
    
    按顺序的网络请求
    dispatch_semaphore_t semaphoreNew = dispatch_semaphore_create(0);
        dispatch_queue_t globalQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
        dispatch_async(globalQueue, ^{
            for (int i = 0; i < array.count; i++) {
                NSString *uid = array[I];
                TYLog(@"start %d",i);
                NSMutableDictionary *workflowParamsDic = [NSMutableDictionary dictionaryWithCapacity:0];
                TYDIC(workflowParamsDic, @"WorkflowInstanceId", uid);
                NSURLSessionDataTask *task = [[TYNetClient sharedClient] httpGetUseCache:YES params:workflowParamsDic module:@"GetConstructionList" showLoading:nil onView:nil completion:^(TYBaseResultModel *result) {
                    TYLog(@"%d",i);
                    dispatch_semaphore_signal(semaphoreNew);
                } error:^(NSError *error) {
                    dispatch_semaphore_signal(semaphoreNew);
                }];
    //            NSLog(@"Test serial %d %@",i, task);
                dispatch_semaphore_wait(semaphoreNew, DISPATCH_TIME_FOREVER);
                if (i == 3) {
                    break;
                }
            }
        });
    

    Dispatch Sources调度源

    与您手动提交给队列的任务不同,调度源为您的应用程序提供了连续的事件源。调度源仍然附加到其调度队列,直到您明确取消它。在附加时,只要相应的事件发生,它就会将相关的任务代码提交给调度队列。某些事件(如计时器事件)会定期发生,但大多数情况下只会在特定条件出现时偶发出现。因此,调度源保留其关联的调度队列,以防止事件过早释放,同时事件可能仍在等待处理。

    为防止事件在调度队列中积压,调度源实施事件合并方案。如果新事件在事件处理程序出现并执行之前到达,则调度源会将新事件数据中的数据与旧事件中的数据合并。根据事件的类型,合并可能会取代旧事件或更新其保存的信息。例如,基于信号的调度源仅提供关于最新信号的信息,但也报告自从最后一次调用事件处理程序以来已传送了多少总信号。

    GCD支持以下类型的调度源
    • Timer dispatch sources定时器调度源产生定期通知。
    • Signal dispatch sources信号调度源在UNIX信号到达时通知您。
    • Descriptor sources描述符来源通知您各种基于文件和基于套接字的操作,例如:
      • 数据可供阅读时
      • 当可以写入数据时
      • 在文件系统中删除,移动或重命名文件时
      • 文件元信息发生变化时
    • Process dispatch sources流程调度源通知您与流程相关的事件,例如:
      • 当一个进程退出
      • 当一个进程发出一种fork或exec一种呼叫
      • 当一个信号被传递给过程时
    • Mach port dispatch sources通知你Mach-related相关的事件。
    • 自定义调度来源是您定义并触发自己的来源。
    创建调度员的步骤
    1. 使用dispatch_source_create函数创建调度源。
    2. 配置调度源:
      • 为调度源分配一个事件处理程序,使用dispatch_source_set_event_handlerdispatch_source_set_event_handler_f函数
      • 对于定时器源,使用dispatch_source_set_timer功能设置定时器信息
    3. 可选择地将一个取消处理程序分配给调度源,使用函数dispatch_source_set_cancel_handler或者dispatch_source_set_cancel_handlerf
    4. 调用dispatch_resume函数开始处理事件

    由于调度源在使用之前需要一些额外的配置,因此该dispatch_source_create函数将使调度源处于挂起状态。暂停时,调度源会收到事件但不处理它们。这为您提供了安装事件处理程序和执行处理实际事件所需的任何其他配置的时间。

    从调度源获取数据
    Snip20180615_1.png
    创建调度员demo
    dispatch_source_t source = dispatch_source_create(DISPATCH_SOURCE_TYPE_READ,
                                     myDescriptor, 0, myQueue);
    dispatch_source_set_event_handler(source, ^{
       // Get some data from the source variable, which is captured
       // from the parent context.
       size_t estimated = dispatch_source_get_data(source);
     
       // Continue reading the descriptor...
    });
    dispatch_resume(source);
    
    安装取消处理程序

    取消处理程序用于在发布之前清理调度源。对于大多数类型的调度源,取消处理程序是可选的,并且只有在您需要更新与调度源相关的一些自定义行为时才是必需的。但是,对于使用描述符或Mach端口的调度源,您必须提供一个取消处理程序来关闭描述符或释放Mach端口。如果不这样做,可能会导致代码中或系统的其他部分无意中重复使用这些结构,从而导致代码中出现细微的错误。

    dispatch_source_set_cancel_handler(mySource, ^{
       close(fd); // Close a file descriptor opened earlier.
    });
    
    修改目标队列

    虽然在创建调度员的时候指定了运行时间和取消处理程序的队列,但是可以使用dispatch_set_target_queue函数更改队列。
    更改调度源的队列是异步操作,调度源尽可能快地进行更改。如果事件处理程序已经排队等待处理,它将在前一个队列上执行。但是,您在进行更改时到达的其他事件可能会在任一队列中处理。

    将自定义数据和调度源相关联

    像Grand Central Dispatch中的许多其他数据类型一样,您可以使用该dispatch_set_context函数将自定义数据与调度源相关联。您可以使用上下文指针来存储事件处理程序处理事件所需的任何数据。如果你存储在上下文指针任何自定义数据,您还应该安装的取消处理程序来释放数据。

    如果您使用块实现事件处理程序,则还可以捕获局部变量并在基于块的代码中使用它们。尽管这可能会缓解将数据存储在调度源的上下文指针中的需要,但您应该始终谨慎使用此功能。由于调度源可能在应用程序中长期存在,因此在捕获包含指针的变量时应该小心。如果指针指向的数据可以随时解除分配,则应该复制数据或保留数据以防止发生这种情况。无论哪种情况,您都需要提供一个取消处理程序以便稍后释放数据。

    ######## 调度源的内存管理
    像其他调度对象一样,调度源是引用计数数据类型。调度源的初始引用计数为1,可以使用dispatch_retaindispatch_release函数保留和释放。当队列的引用计数达到零时,系统会自动释放分派源数据结构。

    由于它们的使用方式,调度源的所有权可以在调度源本身内部或外部进行管理。通过外部所有权,另一个对象或代码片段将获取调度源的所有权,并负责在不再需要时释放它。在内部拥有的情况下,调度源拥有自己,并负责在适当的时候自行释放。尽管外部所有权非常普遍,但如果您希望创建自主调度源并让它在没有任何进一步交互的情况下管理代码的某些行为,您可能会使用内部所有权。例如,如果调度源被设计为响应单个全局事件,则可能需要它处理该事件,然后立即退出。

    定时器demo
    • 当你设置你的定时器调度源使用dispatch_walltime功能,定时器调度源将其开机时间追踪到挂钟时间。
    • 该计时器每30秒触发一次并具有1秒的余量值。由于定时器间隔相对较大,因此使用该dispatch_walltime功能创建调度源。定时器首次触发,随后的事件每隔30秒到达一次。
    • 即使你指定一个余量值为0,你也绝不应该期望一个定时器在你要求的确切纳秒下触发。系统尽最大努力满足您的需求,但不能保证准确的开火时间。
    dispatch_source_t CreateDispatchTimer(uint64_t interval,
                  uint64_t leeway,
                  dispatch_queue_t queue,
                  dispatch_block_t block)
    {
       dispatch_source_t timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER,
                                                         0, 0, queue);
       if (timer)
       {
          dispatch_source_set_timer(timer, dispatch_walltime(NULL, 0), interval, leeway);
          dispatch_source_set_event_handler(timer, block);
          dispatch_resume(timer);
       }
       return timer;
    }
     
    void MyCreateTimer()
    {
       dispatch_source_t aTimer = CreateDispatchTimer(30ull * NSEC_PER_SEC,
                                   1ull * NSEC_PER_SEC,
                                   dispatch_get_main_queue(),
                                   ^{ MyPeriodicTask(); });
     
       // Store it somewhere for later use.
        if (aTimer)
        {
            MyStoreTimer(aTimer);
        }
    }
    
    从描述符读取数据Reading Data from a Descriptor

    要从文件或socket读取数据,您必须打开文件或socket并创建一个DISPATCH_SOURCE_TYPE_READ类型的调度源。

    dispatch_source_t ProcessContentsOfFile(const char* filename)
    {
       // Prepare the file for reading.
       int fd = open(filename, O_RDONLY);
       if (fd == -1)
          return NULL;
       fcntl(fd, F_SETFL, O_NONBLOCK);  // Avoid blocking the read operation避免阻止读取操作
     
       dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
       dispatch_source_t readSource = dispatch_source_create(DISPATCH_SOURCE_TYPE_READ,
                                       fd, 0, queue);
       if (!readSource)
       {
          close(fd);
          return NULL;
       }
     
       // Install the event handler
       dispatch_source_set_event_handler(readSource, ^{
          size_t estimated = dispatch_source_get_data(readSource) + 1;
          // Read the data into a text buffer.
          char* buffer = (char*)malloc(estimated);
          if (buffer)
          {
             ssize_t actual = read(fd, buffer, (estimated));
             Boolean done = MyProcessFileData(buffer, actual);  // Process the data.
     
             // Release the buffer when done.
             free(buffer);
     
             // If there is no more data, cancel the source.
             if (done)
                dispatch_source_cancel(readSource);
          }
        });
     
       // Install the cancellation handler
       dispatch_source_set_cancel_handler(readSource, ^{close(fd);});
     
       // Start reading the file.
       dispatch_resume(readSource);
       return readSource;
    }
    
    将数据写入描述符Writing Data to a Descriptor

    将数据写入文件或套接字的过程与读取数据的过程非常相似。在为写入操作配置描述符后,您将创建一个DISPATCH_SOURCE_TYPE_WRITE类型的调度源。一旦创建了该调度源,系统会调用您的事件处理程序,使其有机会开始将数据写入文件或套接字。完成数据写入后,使用该dispatch_source_cancel功能取消调度源。

    dispatch_source_t WriteDataToFile(const char* filename)
    {
        int fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC,
                      (S_IRUSR | S_IWUSR | S_ISUID | S_ISGID));
        if (fd == -1)
            return NULL;
        fcntl(fd, F_SETFL); // Block during the write.
        
        dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
        dispatch_source_t writeSource = dispatch_source_create(DISPATCH_SOURCE_TYPE_WRITE,
                                                               fd, 0, queue);
        if (!writeSource)
        {
            close(fd);
            return NULL;
        }
        
        dispatch_source_set_event_handler(writeSource, ^{
            size_t bufferSize = MyGetDataSize();
            void* buffer = malloc(bufferSize);
            
            size_t actual = MyGetData(buffer, bufferSize);
            write(fd, buffer, actual);
            
            free(buffer);
            
            // Cancel and release the dispatch source when done.
            dispatch_source_cancel(writeSource);
        });
        
        dispatch_source_set_cancel_handler(writeSource, ^{close(fd);});
        dispatch_resume(writeSource);
        return (writeSource);
    }
    
    监视文件系统对象

    如果要监视文件系统对象的更改,可以设置DISPATCH_SOURCE_TYPE_VNODE类型的调度源。当文件被删除,写入或重命名时,您可以使用这种类型的调度源来接收通知。当文件的特定类型的元信息(如大小和链接数量)发生变化时,您也可以使用它来提醒用户。

    dispatch_source_t MonitorNameChangesToFile(const char* filename)
    {
        int fd = open(filename, O_EVTONLY);
        if (fd == -1)
            return NULL;
        
        dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
        dispatch_source_t source = dispatch_source_create(DISPATCH_SOURCE_TYPE_VNODE,
                                                          fd, DISPATCH_VNODE_RENAME, queue);
        if (source)
        {
            // Copy the filename for later use.
            int length = strlen(filename);
            char* newString = (char*)malloc(length + 1);
            newString = strcpy(newString, filename);
            dispatch_set_context(source, newString);
            
            // Install the event handler to process the name change
            dispatch_source_set_event_handler(source, ^{
                const char*  oldFilename = (char*)dispatch_get_context(source);
                MyUpdateFileName(oldFilename, fd);
            });
            
            // Install a cancellation handler to free the descriptor
            // and the stored string.
            dispatch_source_set_cancel_handler(source, ^{
                char* fileStr = (char*)dispatch_get_context(source);
                free(fileStr);
                close(fd);
            });
            
            // Start processing events.
            dispatch_resume(source);
        }
        else
            close(fd);
        
        return source;
    }
    
    监测信号
    void InstallSignalHandler()
    {
        // Make sure the signal does not terminate the application.
        signal(SIGHUP, SIG_IGN);
        
        dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
        dispatch_source_t source = dispatch_source_create(DISPATCH_SOURCE_TYPE_SIGNAL, SIGHUP, 0, queue);
        
        if (source)
        {
            dispatch_source_set_event_handler(source, ^{
                MyProcessSIGHUP();
            });
            
            // Start processing signals
            dispatch_resume(source);
        }
    }
    
    监视进程
    void MonitorParentProcess()
    {
        pid_t parentPID = getppid();
        
        dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
        dispatch_source_t source = dispatch_source_create(DISPATCH_SOURCE_TYPE_PROC,
                                                          parentPID, DISPATCH_PROC_EXIT, queue);
        if (source)
        {
            dispatch_source_set_event_handler(source, ^{
                MySetAppExitFlag();
                dispatch_source_cancel(source);
                dispatch_release(source);
            });
            dispatch_resume(source);
        }
    }
    
    取消调度源
    void RemoveDispatchSource(dispatch_source_t mySource)
    {
       dispatch_source_cancel(mySource);
       dispatch_release(mySource);
    }
    
    dispatch_barrier_sync和dispatch_barrier_async

    这两个方法不能用于全局队列
    共同点:
    1、等待在它前面插入队列的任务先执行完
    2、等待他们自己的任务执行完再执行后面的任务
    不同点:
    1、dispatch_barrier_sync将自己的任务插入到队列的时候,需要等待自己的任务结束之后才会继续插入被写在它后面的任务,然后执行它们
    2、dispatch_barrier_async将自己的任务插入到队列之后,不会等待自己的任务结束,它会继续把后面的任务插入到队列,然后等待自己的任务结束后才执行后面任务。

    相关文章

      网友评论

          本文标题:GCD

          本文链接:https://www.haomeiwen.com/subject/ajkvdftx.html