OC 多线程GCD

参考: GCD源码 深入理解 GCD iOS多线程--彻底学会多线程之『GCD』 关于iOS多线程,我说,你听,没准你就懂了

####任务执行方式

  • 同步执行(dispatch_sync):只能在当前线程中执行任务,不具备开启新线程的能力。必须等到Block函数执行完毕后,dispatch函数才会返回。
  • 异步执行(dispatch_async):可以在新的线程中执行任务,具备开启新线程的能力。dispatch函数会立即返回, 然后Block在后台异步执行。

####任务管理方式

  • 串行队列:所有任务会在一条线程中执行(有可能是当前线程也有可能是新开辟的线程),并且一个任务执行完毕后,才开始执行下一个任务。(等待完成)
  • 并行队列:可以开启多条线程并行执行任务(但不一定会开启新的线程),并且当一个任务放到指定线程开始执行时,下一个任务就可以开始执行了。(等待发生)
    // 主队列--串行,所有放在主队列中的任务,都会放到主线程中执行
    dispatch_queue_t mainQueue = dispatch_get_main_queue();
    
    // 全局队列--并行,系统为我们创建好的一个并行队列,使用起来与我们自己创建的并行队列无本质差别
    dispatch_queue_t globalQueue = dispatch_get_global_queue(0, 0);
    
    // new串行队列
    dispatch_queue_t queue1 = dispatch_queue_create("test.queue", DISPATCH_QUEUE_SERIAL);
    
    // new并行队列
    dispatch_queue_t queue2 = dispatch_queue_create("test.queue", DISPATCH_QUEUE_CONCURRENT);
复制代码

注意:避免使用 GCD Global队列创建Runloop常驻线程 全局队列的底层是一个线程池,向全局队列中提交的 block,都会被放到这个线程池中执行,如果线程池已满,后续再提交 block 就不会再重新创建线程。等待有空闲的线程在执行任务。 所以: 避免使用 GCD Global 队列创建 Runloop 常驻线程,如果n条线程都被霸占了,Global队列就费了。

####任务+队列

串行队列并行队列主队列
同步(sync)当前线程,串行执行队列当前线程,串行执行主新线程,串行执行(注意死锁)
异步(async)开1条新线程,串行执行开n条新线程,异步执行(n在iphone7上面最大是几十个)主新线程,串行执行

####Dispatch Block

队列执行任务都是block的方式,

######创建block

- (void)createDispatchBlock {
    // 一般的block
    dispatch_queue_t concurrentQueue = dispatch_queue_create("com.starming.gcddemo.concurrentqueue",DISPATCH_QUEUE_CONCURRENT);
    dispatch_block_t block = dispatch_block_create(0, ^{
        NSLog(@"run block");
    });
    dispatch_async(concurrentQueue, block);

    //QOS优先级的block
    dispatch_block_t qosBlock = dispatch_block_create_with_qos_class(0, QOS_CLASS_USER_INITIATED, -1, ^{
        NSLog(@"run qos block");
    });
    dispatch_async(concurrentQueue, qosBlock);
}
复制代码

dispatch_block_wait:可以根据dispatch block来设置等待时间,参数DISPATCH_TIME_FOREVER会一直等待block结束

- (void)dispatchBlockWaitDemo {
    dispatch_queue_t serialQueue = dispatch_queue_create("com.starming.gcddemo.serialqueue", DISPATCH_QUEUE_SERIAL);
    dispatch_block_t block = dispatch_block_create(0, ^{
        NSLog(@"star");
        [NSThread sleepForTimeInterval:5.f];
        NSLog(@"end");
    });
    dispatch_async(serialQueue, block);
    //设置DISPATCH_TIME_FOREVER会一直等到前面任务都完成
    dispatch_block_wait(block, DISPATCH_TIME_FOREVER);
    NSLog(@"ok, now can go on");
}
复制代码

dispatch_block_notify:可以监视指定dispatch block结束,然后再加入一个block到队列中。三个参数分别为,第一个是需要监视的block,第二个参数是需要提交执行的队列,第三个是待加入到队列中的block

- (void)dispatchBlockNotifyDemo {
    dispatch_queue_t serialQueue = dispatch_queue_create("com.starming.gcddemo.serialqueue", DISPATCH_QUEUE_SERIAL);
    dispatch_block_t firstBlock = dispatch_block_create(0, ^{
        NSLog(@"first block start");
        [NSThread sleepForTimeInterval:2.f];
        NSLog(@"first block end");
    });
    dispatch_async(serialQueue, firstBlock);
    dispatch_block_t secondBlock = dispatch_block_create(0, ^{
        NSLog(@"second block run");
    });
    //first block执行完才在serial queue中执行second block
    dispatch_block_notify(firstBlock, serialQueue, secondBlock);
}
复制代码

dispatch_block_cancel:iOS8之后可以调用dispatch_block_cancel来取消(需要注意必须用dispatch_block_create创建dispatch_block_t) 需要注意的是,未执行的可以用此方法cancel掉,若已经执行则cancel不了 如果想中断(interrupt)线程,可以使用dispatch_block_testcancel方法

- (void)dispatchBlockCancelDemo {
    dispatch_queue_t serialQueue = dispatch_queue_create("com.starming.gcddemo.serialqueue", DISPATCH_QUEUE_SERIAL);
    dispatch_block_t firstBlock = dispatch_block_create(0, ^{
        NSLog(@"first block start");
        [NSThread sleepForTimeInterval:2.f];
        NSLog(@"first block end");
    });
    dispatch_block_t secondBlock = dispatch_block_create(0, ^{
        NSLog(@"second block run");
    });
    dispatch_async(serialQueue, firstBlock);
    dispatch_async(serialQueue, secondBlock);
    //取消secondBlock
    dispatch_block_cancel(secondBlock);
}

复制代码

#####1. 串行队列 + 同步执行 不会开启新线程,在当前线程执行任务。任务是串行的,执行完一个任务,再执行下一个任务

- (void)serialQueueSync{
    
    NSLog(@"0========%@",[NSThread currentThread]);
    dispatch_queue_t serialQueue = dispatch_queue_create("", DISPATCH_QUEUE_SERIAL);
    dispatch_sync(serialQueue, ^{
        NSLog(@"1========%@",[NSThread currentThread]);
    });
    dispatch_sync(serialQueue, ^{
        NSLog(@"2========%@",[NSThread currentThread]);
    });
    dispatch_sync(serialQueue, ^{
        NSLog(@"3========%@",[NSThread currentThread]);
    });
    NSLog(@"4========%@",[NSThread currentThread]);
    
    /*
     NSLog输出
     0========<NSThread: 0x60800007f840>{number = 3, name = (null)}
     1========<NSThread: 0x60800007f840>{number = 3, name = (null)}
     2========<NSThread: 0x60800007f840>{number = 3, name = (null)}
     3========<NSThread: 0x60800007f840>{number = 3, name = (null)}
     4========<NSThread: 0x60800007f840>{number = 3, name = (null)}
     */
     
}
复制代码

#####2. 串行队列 + 异步执行 开一个新线程,一个一个执行任务

    NSLog(@"0========%@",[NSThread currentThread]);
    dispatch_queue_t queue = dispatch_queue_create("", DISPATCH_QUEUE_SERIAL);
    dispatch_async(queue, ^{
        NSLog(@"1========%@",[NSThread currentThread]);
    });
    dispatch_async(queue, ^{
        NSLog(@"2========%@",[NSThread currentThread]);
    });
    dispatch_async(queue, ^{
        NSLog(@"3========%@",[NSThread currentThread]);
    });
    NSLog(@"4========%@",[NSThread currentThread]);
    
    /*
     NSLog输出
     0========<NSThread: 0x6000002616c0>{number = 1, name = main}
     4========<NSThread: 0x6000002616c0>{number = 1, name = main}
     1========<NSThread: 0x608000270540>{number = 3, name = (null)}
     2========<NSThread: 0x608000270540>{number = 3, name = (null)}
     3========<NSThread: 0x608000270540>{number = 3, name = (null)}
     */
复制代码

#####3. 并行队列 + 同步执行 当前线程,一个一个执行任务

    NSLog(@"0========%@",[NSThread currentThread]);
    dispatch_queue_t queue = dispatch_queue_create("", DISPATCH_QUEUE_CONCURRENT);
    dispatch_sync(queue, ^{
        NSLog(@"1========%@",[NSThread currentThread]);
    });
    dispatch_sync(queue, ^{
        NSLog(@"2========%@",[NSThread currentThread]);
    });
    dispatch_sync(queue, ^{
        NSLog(@"3========%@",[NSThread currentThread]);
    });
    NSLog(@"4========%@",[NSThread currentThread]);
    
    /*
     NSLog输出
     0========<NSThread: 0x60800007f840>{number = 3, name = (null)}
     1========<NSThread: 0x60800007f840>{number = 3, name = (null)}
     2========<NSThread: 0x60800007f840>{number = 3, name = (null)}
     3========<NSThread: 0x60800007f840>{number = 3, name = (null)}
     4========<NSThread: 0x60800007f840>{number = 3, name = (null)}
     */
复制代码

#####4. 并行队列 + 异步执行

开多个线程,异步执行

    NSLog(@"0========%@",[NSThread currentThread]);
    dispatch_queue_t queue = dispatch_queue_create("", DISPATCH_QUEUE_CONCURRENT);
    dispatch_async(queue, ^{
        NSLog(@"1========%@",[NSThread currentThread]);
    });
    dispatch_async(queue, ^{
        NSLog(@"2========%@",[NSThread currentThread]);
    });
    dispatch_async(queue, ^{
        NSLog(@"3========%@",[NSThread currentThread]);
    });
    NSLog(@"4========%@",[NSThread currentThread]);
    
    /*
     NSLog输出
     0========<NSThread: 0x608000070300>{number = 1, name = main}
     4========<NSThread: 0x608000070300>{number = 1, name = main}
     2========<NSThread: 0x608000264140>{number = 4, name = (null)}
     1========<NSThread: 0x60000007a800>{number = 3, name = (null)}
     3========<NSThread: 0x6080002642c0>{number = 5, name = (null)}
     */
复制代码

#####5. 主队列 + 异步执行

主线程,同步执行

    NSLog(@"0========%@",[NSThread currentThread]);
    dispatch_queue_t queue = dispatch_get_main_queue();
    dispatch_async(queue, ^{
        NSLog(@"1========%@",[NSThread currentThread]);
    });
    dispatch_async(queue, ^{
        NSLog(@"2========%@",[NSThread currentThread]);
    });
    dispatch_async(queue, ^{
        NSLog(@"3========%@",[NSThread currentThread]);
    });
    NSLog(@"4========%@",[NSThread currentThread]);
    
    /*
     NSLog输出
     0========<NSThread: 0x60000026e000>{number = 3, name = (null)}
     4========<NSThread: 0x60000026e000>{number = 3, name = (null)}
     1========<NSThread: 0x60000007e2c0>{number = 1, name = main}
     2========<NSThread: 0x60000007e2c0>{number = 1, name = main}
     3========<NSThread: 0x60000007e2c0>{number = 1, name = main}
     */
复制代码

#####6. 主队列 + 同步执行 (不能在主队列这么用,死锁) 主线程,同步执行

    NSLog(@"0========%@",[NSThread currentThread]);
    dispatch_queue_t queue = dispatch_get_main_queue();
    dispatch_sync(queue, ^{
        NSLog(@"1========%@",[NSThread currentThread]);
    });
    dispatch_sync(queue, ^{
        NSLog(@"2========%@",[NSThread currentThread]);
    });
    dispatch_sync(queue, ^{
        NSLog(@"3========%@",[NSThread currentThread]);
    });
    NSLog(@"4========%@",[NSThread currentThread]);
    
    /*
     NSLog输出
     0========<NSThread: 0x600000263840>{number = 3, name = (null)}
     1========<NSThread: 0x608000078ec0>{number = 1, name = main}
     2========<NSThread: 0x608000078ec0>{number = 1, name = main}
     3========<NSThread: 0x608000078ec0>{number = 1, name = main}
     4========<NSThread: 0x600000263840>{number = 3, name = (null)}
     */
复制代码

####GCD其他用法 #####dispatch_after延时 1、time = 0,是直接调用异步dispatch_async 2、time > 0, 只是延时提交block,不是延时立刻执行。

    //2秒延时、在主队列
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 2.0 * NSEC_PER_SEC), dispatch_get_main_queue(), ^{
        
    });
复制代码

#####dispatch_once与dispatch_once_t

    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        //
    });
复制代码

1、dispatch_once并不是简单的只执行一次那么简单 2、dispatch_once本质上可以接受多次请求,会对此维护一个请求链表 3、如果在block执行期间,多次进入调用同类的dispatch_once函数(即单例函数),会导致整体链表无限增长,造成永久性死锁。

递归互相嵌套,如下:

- (void)viewDidLoad {
    [super viewDidLoad];
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{ // 链表无限增长
        [self viewDidLoad];
    });
}
复制代码

dispatch_once源码

static void
dispatch_once_f_slow(dispatch_once_t *val, void *ctxt, dispatch_function_t func)
{
#if DISPATCH_GATE_USE_FOR_DISPATCH_ONCE
	dispatch_once_gate_t l = (dispatch_once_gate_t)val;

	if (_dispatch_once_gate_tryenter(l)) {
		_dispatch_client_callout(ctxt, func);
		_dispatch_once_gate_broadcast(l);
	} else {
		_dispatch_once_gate_wait(l);
	}
#else
	_dispatch_once_waiter_t volatile *vval = (_dispatch_once_waiter_t*)val;
	struct _dispatch_once_waiter_s dow = { };
	_dispatch_once_waiter_t tail = &dow, next, tmp;
	dispatch_thread_event_t event;


	if (os_atomic_cmpxchg(vval, NULL, tail, acquire)) {
		
		// 第一次dispatch_once,原子性操作
		
		// 当前线程
		dow.dow_thread = _dispatch_tid_self();
		// 执行block
		_dispatch_client_callout(ctxt, func);

		// 第一次执行完了,设置token = DISPATCH_ONCE_DONE
		next = (_dispatch_once_waiter_t)_dispatch_once_xchg_done(val);
		while (next != tail) {
			
			// 继续去下一个
			tmp = (_dispatch_once_waiter_t)_dispatch_wait_until(next->dow_next);
			event = &next->dow_event;
			next = tmp;
			
			// 信号量++
			_dispatch_thread_event_signal(event);
		}
	} else {
		
		// 第二次dispatch_once进来
		_dispatch_thread_event_init(&dow.dow_event);
		next = *vval;
		for (;;) {
			if (next == DISPATCH_ONCE_DONE) { // token是否等于DISPATCH_ONCE_DONE
				// 第一次执行完之后,都是走这里
				break;
			}
			// 如果是嵌套使用,第一次没有完成,又要执行一次
			if (os_atomic_cmpxchgv(vval, next, tail, &next, release)) {
				// 原子性
				dow.dow_thread = next->dow_thread;
				dow.dow_next = next;
				if (dow.dow_thread) {
					pthread_priority_t pp = _dispatch_get_priority();
					_dispatch_thread_override_start(dow.dow_thread, pp, val);
				}
				// 等待信号量
				_dispatch_thread_event_wait(&dow.dow_event);
				if (dow.dow_thread) {
					_dispatch_thread_override_end(dow.dow_thread, val);
				}
				break;
			}
		}
		_dispatch_thread_event_destroy(&dow.dow_event);
	}
#endif
}
复制代码

#####dispatch_apply(count,queue,block(index))迭代方法 该函数按指定的次数将指定的block追加到指定的队列;使用的地方,阻塞当前线程

    NSLog(@"CurrentThread------%@", [NSThread currentThread]);
    //dispatch_queue_t queue = dispatch_get_global_queue(0,0);
    dispatch_queue_t queue = dispatch_queue_create("test.queue", DISPATCH_QUEUE_SERIAL);
    // 6是次数
    dispatch_apply(6, queue, ^(size_t index) {
        NSLog(@"%zd------%@",index, [NSThread currentThread]);
    });
    /*
     并发队列:开多线程异步执行
     NSLogx信息
     CurrentThread------<NSThread: 0x600000268a40>{number = 3, name = (null)}
     0------<NSThread: 0x600000268a40>{number = 3, name = (null)}
     1------<NSThread: 0x608000266e40>{number = 4, name = (null)}
     2------<NSThread: 0x608000266f00>{number = 5, name = (null)}
     3------<NSThread: 0x608000266f40>{number = 6, name = (null)}
     4------<NSThread: 0x600000268a40>{number = 3, name = (null)}
     5------<NSThread: 0x608000266e40>{number = 4, name = (null)}
     */
    
    
    /*
     同步队列:当前线程同步执行
     NSLogx信息
     CurrentThread------<NSThread: 0x608000072c00>{number = 3, name = (null)}
     0------<NSThread: 0x6000000694c0>{number = 1, name = main}
     1------<NSThread: 0x6000000694c0>{number = 1, name = main}
     2------<NSThread: 0x6000000694c0>{number = 1, name = main}
     3------<NSThread: 0x6000000694c0>{number = 1, name = main}
     4------<NSThread: 0x6000000694c0>{number = 1, name = main}
     5------<NSThread: 0x6000000694c0>{number = 1, name = main}
     */
复制代码

dispatch_apply能避免线程爆炸,因为GCD会管理并发

dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
for (int i = 0; i < 999; i++){
      dispatch_async(queue, ^{
         NSLog(@"%d,%@",i,[NSThread currentThread]);// 能开多大线程就开多大线程(几十个)
      });
}
复制代码
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_apply(999, queue, ^(size_t i){
     NSLog(@"%d,%@",i,[NSThread currentThread]); // 只开一定数量的线程(几个)
});
复制代码

#####dispatch_suspend、dispatch_resume (用在dispatch_get_global_queue主队列无效)

dispatch_suspend,dispatch_resume提供了“挂起、恢复”队列的功能,简单来说,就是可以暂停、恢复队列上的任务。但是这里的“挂起”,并不能保证可以立即停止队列上正在运行的block

注意点: ######1、如果队列没有使用dispatch_suspend,使用dispatch_resume会crash

- (void)viewDidLoad {
    [super viewDidLoad];
    
    dispatch_queue_t queue = dispatch_queue_create("", DISPATCH_QUEUE_CONCURRENT);
    dispatch_resume(queue); // crash
}
复制代码

######2、如果queue被挂起,queue销毁时候没有被唤醒,会crash

        dispatch_queue_t queue = dispatch_queue_create("", DISPATCH_QUEUE_CONCURRENT);
        dispatch_suspend(queue);// 如果queue被挂起,queue销毁时候没有被唤醒,会crash
复制代码

######3、dispatch_suspend后面执行dispatch_sync,阻塞当前线程,需要其他线程恢复队列

        queue = dispatch_queue_create("", DISPATCH_QUEUE_CONCURRENT);
        dispatch_suspend(queue);
        // 后面执行dispatch_sync,阻塞当前线程,需要其他线程恢复队列
        dispatch_sync(queue, ^{
            NSLog(@"=====%@",@"111111");
        });
        NSLog(@"=====%@",@"22222");
复制代码

####GCD的队列组dispatch_group_t,其实就是封装了一个无限大的信号量, 注意事项 1、dispatch_group_async(只有async,无sync)等价于{dispatch_group_enter() + async}, async调用完了会执行dispatch_group_leave()。 2、dispatch_group_enter()就是信号量--; 3、dispatch_group_leave()就是信号量++ 4、dispatch_group_enter() 必须运行在 dispatch_group_leave() 之前。 5、dispatch_group_enter() 和 dispatch_group_leave() 需要成对出现的

    //1.创建队列组
    dispatch_group_t group = dispatch_group_create();

    //2.1.全局队列
    dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        for (NSInteger i = 0; i < 3; i++) {
            NSLog(@"group-01 - %@", [NSThread currentThread]);
        }
    });
    
    //2.2.主队列
    dispatch_group_async(group, dispatch_get_main_queue(), ^{
        for (NSInteger i = 0; i < 3; i++) {
            NSLog(@"group-02 - %@", [NSThread currentThread]);
        }
    });
   
    //2.3.自建串行队列
    dispatch_group_async(group, dispatch_queue_create("test.queue", DISPATCH_QUEUE_SERIAL), ^{
        for (NSInteger i = 0; i < 3; i++) {
            NSLog(@"group-03 - %@", [NSThread currentThread]);
        }
    });
    
    //3.都完成后会自动通知,不阻塞当前线程
    dispatch_group_notify(group, dispatch_get_main_queue(), ^{
        NSLog(@"完成 - %@", [NSThread currentThread]);
    });
    NSLog(@"当前线程=====%@",[NSThread currentThread]);
    
     /*
      并行队列、自建串行队列的任务多线程异步执行
      主队列的任务主线程同步执行,且排在全部任务的最后
      
      NSLog信息
      当前线程=====<NSThread: 0x60000007c240>{number = 1, name = main}
      group-01 - <NSThread: 0x60800026d180>{number = 3, name = (null)}
      group-01 - <NSThread: 0x60800026d180>{number = 3, name = (null)}
      group-03 - <NSThread: 0x60000026c7c0>{number = 4, name = (null)}
      group-01 - <NSThread: 0x60800026d180>{number = 3, name = (null)}
      group-03 - <NSThread: 0x60000026c7c0>{number = 4, name = (null)}
      group-03 - <NSThread: 0x60000026c7c0>{number = 4, name = (null)}
      group-02 - <NSThread: 0x60000007c240>{number = 1, name = main}
      group-02 - <NSThread: 0x60000007c240>{number = 1, name = main}
      group-02 - <NSThread: 0x60000007c240>{number = 1, name = main}
      完成 - <NSThread: 0x60000007c240>{number = 1, name = main}
      */
复制代码

#####手动标记group完成

  • dispatch_group_enter(group)
  • dispatch_group_leave(group);

######dispatch_group_notify(不阻塞)想当与把block任务加在最后

NSLog(@"start");
    //1.创建队列组
    dispatch_group_t group = dispatch_group_create();
    for (int i=0; i< 5; i++) {
        dispatch_group_enter(group); // enter
        dispatch_async(dispatch_get_global_queue(0, 0), ^{
            // something
            NSLog(@"something===%zd",i);
            dispatch_group_leave(group); // eave
        });
    }
    // 都完成后会自动通知,不阻塞当前线程
    dispatch_group_notify(group, dispatch_get_main_queue(), ^{
        NSLog(@"完成 - %@", [NSThread currentThread]);
    });
    NSLog(@"end");

     /*
      NSLog信息:
      start
      end
      something===1
      something===0
      something===2
      something===3
      something===4
      完成 - <NSThread: 0x60800006e900>{number = 1, name = main}
      */
    

复制代码

######dispatch_group_wait就是等待group的信号量回到初始值(阻塞当前线程)

    NSLog(@"start");
    //1.创建队列组
    dispatch_group_t group = dispatch_group_create();
    
    for (int i=0; i< 5; i++) {
        
        dispatch_group_enter(group); // enter
        
        dispatch_async(dispatch_get_global_queue(0, 0), ^{
            
            // something
            
            [NSThread sleepForTimeInterval:2];
            NSLog(@"something===%zd",i);
            dispatch_group_leave(group); // eave
        });
        
    }
    // 阻塞当前线程的、等待5秒
    dispatch_time_t waitTime = dispatch_time(DISPATCH_TIME_NOW, 5 * NSEC_PER_SEC);
    dispatch_group_wait(group, waitTime);//特殊值有:DISPATCH_TIME_FOREVER是无限等,DISPATCH_TIME_NOW是不等
    NSLog(@"end");
    
    
     /*
      等待时间 < 执行需要时间
      NSLog信息:
      start
      end
      something===0
      something===1
      something===3
      something===2
      something===4
      */
    
    /*
     等待时间 > 执行需要时间
     NSLog信息:
     start
     something===1
     something===0
     something===4
     something===2
     something===3
     end
     */
复制代码

#####dispatch_semaphore_create & dispatch_semaphore_signal & dispatch_semaphore_wait 信号量是控制任务执行的重要条件,当信号量为0时,所有任务等待,信号量越大,允许可并行执行的任务数量越多。

  • dispatch_semaphore_create(long value);创建信号量,初始值不能小于0;value信号数值
  • dispatch_semaphore_wait(semaphore, timeout);等待降低信号量,也就是信号量-1;timeout不是调用dispatch_semaphore_wait后等待的时间,而是信号量创建后的时间
  • dispatch_semaphore_signal(semaphore);提高信号量,也就是信号量+1;
  • dispatch_semaphore_wait和dispatch_semaphore_signal通常配对使用。
    // 相当于控制新建的线程数
    dispatch_semaphore_t semaphore = dispatch_semaphore_create(10);
    for (int i=0; i< 10; i++) {
        
        dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
        dispatch_async(dispatch_get_global_queue(0, 0), ^{
            
            [NSThread sleepForTimeInterval:1];
            NSLog(@"第%@次_%@",@(i),[NSThread currentThread]);
            dispatch_semaphore_signal(semaphore);
        });
        
    }
    
    
     /*
      dispatch_semaphore_t semaphore = dispatch_semaphore_create(5);
      只有5条线程
      NSLog信息:
      第0次_<NSThread: 0x608000268b00>{number = 4, name = (null)}
      第1次_<NSThread: 0x600000269240>{number = 6, name = (null)}
      第3次_<NSThread: 0x600000269100>{number = 5, name = (null)}
      第2次_<NSThread: 0x608000268ac0>{number = 3, name = (null)}
      第4次_<NSThread: 0x600000269780>{number = 7, name = (null)}
      第8次_<NSThread: 0x608000268ac0>{number = 3, name = (null)}
      第7次_<NSThread: 0x600000269100>{number = 5, name = (null)}
      第6次_<NSThread: 0x608000268b00>{number = 4, name = (null)}
      第5次_<NSThread: 0x600000269240>{number = 6, name = (null)}
      第9次_<NSThread: 0x600000269780>{number = 7, name = (null)}
      */
    
    /*
     dispatch_semaphore_t semaphore = dispatch_semaphore_create(10);
     10条线程
     NSLog信息:
     第2次_<NSThread: 0x6080000661c0>{number = 3, name = (null)}
     第4次_<NSThread: 0x608000073e40>{number = 7, name = (null)}
     第1次_<NSThread: 0x600000079dc0>{number = 4, name = (null)}
     第5次_<NSThread: 0x6000000721c0>{number = 8, name = (null)}
     第3次_<NSThread: 0x608000073dc0>{number = 6, name = (null)}
     第0次_<NSThread: 0x608000073d40>{number = 5, name = (null)}
     第6次_<NSThread: 0x608000073d80>{number = 9, name = (null)}
     第9次_<NSThread: 0x608000073e00>{number = 10, name = (null)}
     第7次_<NSThread: 0x6000000717c0>{number = 11, name = (null)}
     第8次_<NSThread: 0x600000066b40>{number = 12, name = (null)}
     */
复制代码

#####dispatch_barrier_async、dispatch_barrier_sync (承上启下--用于自建的并行队列) 保证此前的任务都先于自己执行,此后的任务也迟于自己执行。 dispatch_barrier_async 不阻塞当前线程; dispatch_barrier_sync 阻塞当前线程;

注意:dispatch_barrier_(a)sync只在自己创建的并发队列上有效,在全局(Global)并发队列、串行队列上,效果跟dispatch_(a)sync效果一样。

- (void)test{

    dispatch_queue_t globalQueue = dispatch_get_global_queue(0, 0);
    dispatch_async(globalQueue, ^{
        NSLog(@"任务1");
    });
    dispatch_async(globalQueue, ^{
        NSLog(@"任务2");
    });
    dispatch_barrier_async(globalQueue, ^{
        NSLog(@"任务barrier");
    });
    dispatch_async(globalQueue, ^{
        NSLog(@"任务3");
    });
    dispatch_async(globalQueue, ^{
        NSLog(@"任务4");
    });
    /*
     2017-09-02 21:03:40.255 NSThreadTest[28856:21431532] 任务2
     2017-09-02 21:03:40.255 NSThreadTest[28856:21431535] 任务1
     2017-09-02 21:03:40.255 NSThreadTest[28856:21431533] 任务barrier
     2017-09-02 21:03:40.255 NSThreadTest[28856:21431551] 任务3
     2017-09-02 21:03:40.256 NSThreadTest[28856:21431550] 任务4
     */
}
复制代码

GCD创建Timer

-(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
    //创建队列
    dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
    //1.创建一个GCD定时器
    /*
     第一个参数:表明创建的是一个定时器
     第四个参数:队列
     */
    dispatch_source_t timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, queue);
    // 需要对timer进行强引用,保证其不会被释放掉,才会按时调用block块
    // 局部变量,让指针强引用
    self.timer = timer;
    //2.设置定时器的开始时间,间隔时间,精准度
    /*
     第1个参数:要给哪个定时器设置
     第2个参数:开始时间
     第3个参数:间隔时间
     第4个参数:精准度 一般为0 在允许范围内增加误差可提高程序的性能
     GCD的单位是纳秒 所以要*NSEC_PER_SEC
     */
    dispatch_source_set_timer(timer, DISPATCH_TIME_NOW, 2.0 * NSEC_PER_SEC, 0 * NSEC_PER_SEC);

    //3.设置定时器要执行的事情
    dispatch_source_set_event_handler(timer, ^{
        NSLog(@"---%@--",[NSThread currentThread]);
    });
    // 启动
    dispatch_resume(timer);
}

复制代码

#####GCD各种死锁的情况

######1、最常见的

- (void)viewDidLoad {
    [super viewDidLoad];
    
    NSLog(@"0========%@",[NSThread currentThread]);// 当前是主线程、主队列
    dispatch_sync(dispatch_get_main_queue(), ^{
        NSLog(@"1========%@",[NSThread currentThread]);
    });
    NSLog(@"2========%@",[NSThread currentThread]);
    
    /*
     NSLog信息
     0========<NSThread: 0x60000007c5c0>{number = 1, name = main}
     
     解析
     1.viewDidLoad是主队列,dispatch_sync也属于主队列,
     2.dispatch_sync是viewDidLoad里面的代码,viewDidLoad需要等待dispatch_sync执行完,dispatch_sync需要等待viewDidLoad执行完,这就死锁了
     */
    
}
复制代码
- (void)viewDidLoad {
    [super viewDidLoad];
    
    NSLog(@"0========%@",[NSThread currentThread]);// 当前是主线程、主队列
    // 改成dispatch_get_global_queue或者new出来的队列
    dispatch_sync(dispatch_get_global_queue(0,0), ^{
        NSLog(@"1========%@",[NSThread currentThread]);
    });
    NSLog(@"2========%@",[NSThread currentThread]);
    
    /*
     NSLog信息
     0========<NSThread: 0x6000000690c0>{number = 1, name = main}
     1========<NSThread: 0x6000000690c0>{number = 1, name = main}
     2========<NSThread: 0x6000000690c0>{number = 1, name = main}
     
     解析
     1.viewDidLoad是主队列,dispatch_sync是global_queue,不在同一队列
     2.dispatch_sync是viewDidLoad里面的代码,viewDidLoad需要等待dispatch_sync执行完返回,但是dispatch_sync不需要等待viewDidLoad执行完,立即执行完返回
     */
    
}
复制代码

######2、串行队列,各种嵌套异步情况 死锁的原因:是同一个串行队列任务内部代码继续嵌套同步sync的任务

        // 串行队列
    dispatch_queue_t queue = dispatch_queue_create("", DISPATCH_QUEUE_SERIAL);
    
    // 同步嵌异步----执行OK
    dispatch_sync(queue, ^{
        dispatch_async(queue, ^{
        });
    });
    
    // 异步嵌异步----执行OK
    dispatch_async(queue, ^{
        dispatch_async(queue, ^{
        });
    });
    
    // 异步嵌同步----死锁
    dispatch_async(queue, ^{
        dispatch_sync(queue, ^{
        });
    });
    
    // 同步嵌同步----死锁
    dispatch_sync(queue, ^{
        dispatch_sync(queue, ^{
        });
    });

复制代码

######3、并行队列,各种嵌套异步情况 并行队列队列各种嵌套都不会死锁

    // 并行队列
    dispatch_queue_t queue = dispatch_queue_create("", DISPATCH_QUEUE_CONCURRENT);
    
    // 同步嵌异步----执行OK
    dispatch_sync(queue, ^{
        dispatch_async(queue, ^{
        });
    });
    
    // 异步嵌异步----执行OK
    dispatch_async(queue, ^{
        dispatch_async(queue, ^{
        });
    });
    
    // 异步嵌同步----执行OK
    dispatch_async(queue, ^{
        dispatch_sync(queue, ^{
        });
    });
    
    // 同步嵌同步----执行OK
    dispatch_sync(queue, ^{
        dispatch_sync(queue, ^{
        });
    });
复制代码

######4、dispatch_apply阻塞当前线程

// 主队列使用,死锁
dispatch_apply(5, dispatch_get_main_queue(), ^(size_t i) {
        NSLog(@"第%@次_%@",@(i),[NSThread currentThread]);
    });
复制代码
// 嵌套使用,死锁
dispatch_queue_t queue = dispatch_queue_create("queue", DISPATCH_QUEUE_CONCURRENT);
dispatch_apply(10, queue, ^(size_t) {
    // 任务
    ...
    dispatch_apply(10, queue, ^(size_t) {
        // 任务
        ...
    });
});
复制代码

dispatch_barrier dispatch_barrier_sync在串行队列和全局并行队列里面和dispatch_sync同样的效果,所以需考虑同dispatch_sync一样的死锁问题。

######5、 信号量阻塞主线程

- (void)viewDidLoad {
    [super viewDidLoad];

    dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
    NSLog(@"semaphore create!");
    dispatch_async(dispatch_get_main_queue(), ^{
        dispatch_semaphore_signal(semaphore);
        NSLog(@"semaphore plus 1");
    });
    dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
    NSLog(@"semaphore minus 1");
}
复制代码

原因: 如果当前执行的线程是主线程,以上代码就会出现死锁。 因为dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER)阻塞了当前线程,而且等待时间是DISPATCH_TIME_FOREVER——永远等待,这样它就永远的阻塞了当前线程——主线程。导致主线中的dispatch_semaphore_signal(semaphore)没有执行, 而dispatch_semaphore_wait一直在等待dispatch_semaphore_signal改变信号量,这样就形成了死锁。

解决方法: 应该将信号量移到并行队列中,如全局调度队列。以下场景,移到串行队列也是可以的。但是串行队列还是有可能死锁的(如果执行dispatch_semaphore_signal方法还是在对应串行队列中的话,即之前提到的串行队列嵌套串行队列的场景)。

- (void)viewDidLoad {
    [super viewDidLoad];
    
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
        NSLog(@"semaphore create!");
        dispatch_async(dispatch_get_main_queue(), ^{
            dispatch_semaphore_signal(semaphore); // +1
            NSLog(@"semaphore plus 1");
        });
        dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
        NSLog(@"semaphore minus 1");
    });
}
复制代码

#####一些嵌套使用问题

    NSLog(@"1");
    dispatch_sync(dispatch_get_global_queue(0, 0), ^{
        NSLog(@"2");
        dispatch_async(dispatch_get_main_queue(), ^{
		   sleep(1);
            NSLog(@"3");
        });
    });
    dispatch_async(dispatch_get_main_queue(), ^{
        NSLog(@"4");
    });
    NSLog(@"5");

    //结果是:12534
    //解析:“2”并发队列同步任务,所以125;“3”、“4”是两个主队列异步,串行执行任务34;最终就是12534
复制代码
    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    dispatch_sync(queue, ^{
        
        dispatch_sync(queue, ^{
            NSLog(@"B");
        });
        NSLog(@"A");
    });
    // 结果是:BA (并发队列不会死锁) 并行队列,任务A加入队列执行中,然后任务B加入队列也立即执行,但是任务A会等任务B先执行完。
复制代码
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值