block
// int num = 10;
// MyBlock block1 = ^{
// NSLog(@"num is %d",num);
// };
// num = 20;
// block1();// 此时打印出来的结果是 10 ;因为 num 在 block 编译时已经作为常量被编译进去了,就不会再被修改
/*
局部的基本数据类型的变量,进入到 block 中, num 会变成常量,如果需要在 block 中对 num 进行修改,需要加上关键字 __block (也可以用 static 关键字进行修饰)
*/
__block int num1 = 10 ;
MyBlock block1 = ^{
NSLog ( @"num1 is %d" ,num1); //20
num1++; //21
};
num1 = 20 ;
block1();
NSLog ( @"num1 is %d" ,num1); //21
/*
局部的 OC 对象进入到 block 时,该对象会被 retain 一次(注意: block 在堆区上时才会起到 retain 作用)
必须进行一次 copy 操作
*/
__block NSObject *obj = [[ NSObject alloc ] init ];
NSLog ( @"%ld" ,obj. retainCount );
MyBlock block2 = ^{
NSLog ( @"%ld" ,obj. retainCount );
};
[block2 copy ]; // 当没有 copy 时, block 块中的 obj.retainCount 为 1 ; copy 之后为 2
// 但是当用 __block 修饰时,即使 copy 后, block 块中的 obj.retainCount 也为 1
block2();
__block Person *ps = [[ Person alloc ] init ]; // 当没有产生循环引用时,可以不使用 __block 修饰,但是一旦产生循环引用,就必须使用 __block 修饰
MyBlock block3 = ^{
NSLog ( @"%ld" ,ps. retainCount );
};
[block3 copy ];
block3();
[ps release ]; // 当没有 __block 修饰时, dealloc 方法不会调用
// MyBlock block1 = ^{
// NSLog(@"num is %d",num);
// };
// num = 20;
// block1();// 此时打印出来的结果是 10 ;因为 num 在 block 编译时已经作为常量被编译进去了,就不会再被修改
/*
局部的基本数据类型的变量,进入到 block 中, num 会变成常量,如果需要在 block 中对 num 进行修改,需要加上关键字 __block (也可以用 static 关键字进行修饰)
*/
__block int num1 = 10 ;
MyBlock block1 = ^{
NSLog ( @"num1 is %d" ,num1); //20
num1++; //21
};
num1 = 20 ;
block1();
NSLog ( @"num1 is %d" ,num1); //21
/*
局部的 OC 对象进入到 block 时,该对象会被 retain 一次(注意: block 在堆区上时才会起到 retain 作用)
必须进行一次 copy 操作
*/
__block NSObject *obj = [[ NSObject alloc ] init ];
NSLog ( @"%ld" ,obj. retainCount );
MyBlock block2 = ^{
NSLog ( @"%ld" ,obj. retainCount );
};
[block2 copy ]; // 当没有 copy 时, block 块中的 obj.retainCount 为 1 ; copy 之后为 2
// 但是当用 __block 修饰时,即使 copy 后, block 块中的 obj.retainCount 也为 1
block2();
__block Person *ps = [[ Person alloc ] init ]; // 当没有产生循环引用时,可以不使用 __block 修饰,但是一旦产生循环引用,就必须使用 __block 修饰
MyBlock block3 = ^{
NSLog ( @"%ld" ,ps. retainCount );
};
[block3 copy ];
block3();
[ps release ]; // 当没有 __block 修饰时, dealloc 方法不会调用
[block3 release];
2.
main函数中
Person
*ps = [[
Person
alloc
]
init
];
[ps
testFun
];
[ps release];
Person的.m文件中
- (
void
)testFun
{
_personObj = [[ NSObject alloc ] init ];
self . myBlock = ^{
NSLog ( @"obj retainCount is %ld" , _personObj . retainCount ); //1 原因:在 block 中引用一个实例变量时,该实例所在的对象会被 retain 一次,而 _personObj 是 ps 的实例变量,所以当在 block 里面引用 _personObj 时, ps 会被 retain 一次,此时 myBlock 会持有 Person 的对象 ps ,而 myBlock 本身是被 Person 的对象 ps 持有,
// 因此就会形成 self--myBlock--self 的循环引用
};
_myBlock ();
_personObj = [[ NSObject alloc ] init ];
self . myBlock = ^{
NSLog ( @"obj retainCount is %ld" , _personObj . retainCount ); //1 原因:在 block 中引用一个实例变量时,该实例所在的对象会被 retain 一次,而 _personObj 是 ps 的实例变量,所以当在 block 里面引用 _personObj 时, ps 会被 retain 一次,此时 myBlock 会持有 Person 的对象 ps ,而 myBlock 本身是被 Person 的对象 ps 持有,
// 因此就会形成 self--myBlock--self 的循环引用
};
_myBlock ();
}
//- (void)testFun
//{
// NSObject *obj = [[NSObject alloc] init];
//
// self.myBlock = ^{
// NSLog(@"obj retainCount is %ld",obj.retainCount);//2
// };
// _myBlock();
//}
/*
在 block 里面引用一个局部的 OC 对象,该对象会被 retain 一次
如果使用 __block 修饰局部变量,则不会被 retain
*/
//- (void)testFun
//{
// __block NSObject *obj = [[NSObject alloc] init];
//
// self.myBlock = ^{
// NSLog(@"obj retainCount is %ld",obj.retainCount);//1,
// };
// _myBlock();
//}
//{
// NSObject *obj = [[NSObject alloc] init];
//
// self.myBlock = ^{
// NSLog(@"obj retainCount is %ld",obj.retainCount);//2
// };
// _myBlock();
//}
/*
在 block 里面引用一个局部的 OC 对象,该对象会被 retain 一次
如果使用 __block 修饰局部变量,则不会被 retain
*/
//- (void)testFun
//{
// __block NSObject *obj = [[NSObject alloc] init];
//
// self.myBlock = ^{
// NSLog(@"obj retainCount is %ld",obj.retainCount);//1,
// };
// _myBlock();
//}
多线程与GCD
程序的执行依赖于主线程
2.进程的基本概念
每一个进程都有一个应用程序,都有独立的内存空间,一般来说一个应用程序存在一个进程,但也有多个进程的情况
同一个进程中的线程共享内存中的内存和资源
3. 多线程的基本概念
每一个程序都有一个主线程,程序启动时创建(调用main函数来启动)
主线程的生命周期是和应用程序绑定的,程序退出(结束)时,主线程也就停止了
多线程技术表示,一个应用程序有多个线程,使用多线程能提高CPU的使用率,防止主线程堵塞
任何有可能堵塞主线程的任务都不要在主线程执行(访问网络)
4. 判断当前线程是否是主线程
BOOL
bool1 = [[
NSThread
currentThread
]
isMainThread
];
NSLog(@"bool1 is %d",bool1);
5.创建线程的六种方式
// //
第一种创建线程的方式
// NSThread *thread1 = [[NSThread alloc] initWithTarget:self selector:@selector(run) object:nil];
// // 第一种方式必须手动开启
// [thread1 start];
// 第二种方式
// [NSThread detachNewThreadSelector:@selector(run) toTarget:self withObject:nil];
// 第三种方式
// [self performSelectorInBackground:@selector(run) withObject:nil];
// 第四种方式 NSOperationQueue 是一个操作队列或线程池
NSOperationQueue *queue = [[ NSOperationQueue alloc ] init ]; // 不能使用当前 queue 即下面一行代码,那样会使得线程变为主线程
// NSOperationQueue *queue = [NSOperationQueue currentQueue];
// // 往队列中添加一个操作
// [queue addOperationWithBlock:^{
// [self run];
// }];
// // 第五种方式 , 往 queue 中添加 operation 但一般不要直接创建 NSOperation 的对象而是创建 NSInvocationOperation 的对象, NSInvocationOperation 继承自 NSOperation
// NSInvocationOperation *operation = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(run) object:nil];
// [queue addOperation:operation];
// 第六种方式 , 子类化 NSOperation ,如果在 PersonOperation 类中没有实现 main 方法,则不会进入到 PersonOperation 类中,因为 main 方法是进入程序的入口
PersonOperation *operation = [[ PersonOperation alloc ] init ];
// NSThread *thread1 = [[NSThread alloc] initWithTarget:self selector:@selector(run) object:nil];
// // 第一种方式必须手动开启
// [thread1 start];
// 第二种方式
// [NSThread detachNewThreadSelector:@selector(run) toTarget:self withObject:nil];
// 第三种方式
// [self performSelectorInBackground:@selector(run) withObject:nil];
// 第四种方式 NSOperationQueue 是一个操作队列或线程池
NSOperationQueue *queue = [[ NSOperationQueue alloc ] init ]; // 不能使用当前 queue 即下面一行代码,那样会使得线程变为主线程
// NSOperationQueue *queue = [NSOperationQueue currentQueue];
// // 往队列中添加一个操作
// [queue addOperationWithBlock:^{
// [self run];
// }];
// // 第五种方式 , 往 queue 中添加 operation 但一般不要直接创建 NSOperation 的对象而是创建 NSInvocationOperation 的对象, NSInvocationOperation 继承自 NSOperation
// NSInvocationOperation *operation = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(run) object:nil];
// [queue addOperation:operation];
// 第六种方式 , 子类化 NSOperation ,如果在 PersonOperation 类中没有实现 main 方法,则不会进入到 PersonOperation 类中,因为 main 方法是进入程序的入口
PersonOperation *operation = [[ PersonOperation alloc ] init ];
[queue addOperation:operation];
重点是第三种、第四种,第一种、第二种必须熟练掌握,五六知道即可
6.
UIImageView
*imgView2 = [[
UIImageView
alloc
]
initWithFrame
:
CGRectMake
(
100
,
250
,
100
,
100
)];
imgView2. backgroundColor = [ UIColor orangeColor ];
[ self . view addSubview :imgView2];
NSString *strUrl2 = @"http://img.zjol.com.cn/pic/0/05/50/64/5506405_892666.jpg" ;
NSURL *url2 = [ NSURL URLWithString :strUrl2];
// imgView2.image = [UIImage imageWithData:[NSData dataWithContentsOfURL:url2]];
[imgView2 setImageWithURL :url2];
UIImageView *imgView3 = [[ UIImageView alloc ] initWithFrame : CGRectMake ( 100 , 400 , 100 , 100 )];
imgView3. backgroundColor = [ UIColor orangeColor ];
[ self . view addSubview :imgView3];
NSString *strUrl3 = @"http://pic.paopaoche.net/up/201405/101240_3814389884471.jpg" ;
NSURL *url3 = [ NSURL URLWithString :strUrl3];
// imgView3.image = [UIImage imageWithData:[NSData dataWithContentsOfURL:url3]];
[imgView3 setImageWithURL :url3];
imgView2. backgroundColor = [ UIColor orangeColor ];
[ self . view addSubview :imgView2];
NSString *strUrl2 = @"http://img.zjol.com.cn/pic/0/05/50/64/5506405_892666.jpg" ;
NSURL *url2 = [ NSURL URLWithString :strUrl2];
// imgView2.image = [UIImage imageWithData:[NSData dataWithContentsOfURL:url2]];
[imgView2 setImageWithURL :url2];
UIImageView *imgView3 = [[ UIImageView alloc ] initWithFrame : CGRectMake ( 100 , 400 , 100 , 100 )];
imgView3. backgroundColor = [ UIColor orangeColor ];
[ self . view addSubview :imgView3];
NSString *strUrl3 = @"http://pic.paopaoche.net/up/201405/101240_3814389884471.jpg" ;
NSURL *url3 = [ NSURL URLWithString :strUrl3];
// imgView3.image = [UIImage imageWithData:[NSData dataWithContentsOfURL:url3]];
[imgView3 setImageWithURL :url3];
1.GCD支持同步或异步任务处理,串行或并行的处理队列,非系统调用的信号量机制,定时任务处理,进程、文件或网络的监听任务等
2.GCD是一项纯C语言、但又溶有面向对象思想、基于block的多线程编程
3.GCD的优点
易用:GCD比之thread更简单易用。基于block的特性导致它能极为简单得在不同代码作用域之间传递上下文
效率:GCD实现功能轻量、优雅,使得它在很多地方比之专门创建消耗资源的线程更实用且快速
性能:GCD自动根据系统负载来增减线程数量,这就减少了上下文切换以及增加了计算效率
安全:无需加锁或其他同步机制
4.Dispatch Queue是执行处理的等待队列。通过dispatch_async等函数,按照先进先出(FIFO)顺序追加到Queue中处理
执行处理时,存在两种Dispatch Queue:
1.Serial Dispatch Queue--等待现在正在执行的任务处理结束(串行)
2.Concurrent Dispatch Queue--不等待现在正在执行的任务处理结束(并行)
5.Serial Dispatch Queue(也称Private Dispatch Queue)
一个线程同时执行一个任务,可以避免数据竞争的问题
可以生成多个Serial Dispatch Queue,各个Serial Dispatch Queue将并行执行
6.Concurrent Dispatch Queue(也称Global Dispatch Queue)
多个线程同时执行多个任务,效率高。具体多少个线程并发执行,取决于CPU核数和CPU负荷
7.一切跟UI有关的操作必须放在主线程中执行
8.两个经典的死锁
//
获取系统的主线程队列
dispatch_queue_t queue = dispatch_get_main_queue();
//
同步添加
// 造成死锁 1
// 在主线程队列中添加了一个任务,因为是同步,所以需要等待任务执行完了才能继续往下走
// 但是新添加的任务要想执行,由于放在队列的末尾,必须要等到添加的操作执行完了才能继续执行
// 因此,程序卡死,不能输出 end
NSLog ( @"begin" );
dispatch_sync(queue, ^{
NSLog( @"sync" );
});
NSLog ( @"end" );
// 解决方案改为 async
[[ NSRunLoop currentRunLoop ] run ];
// 死锁 2
// 外层 在主线程中添加一个任务,因为是异步添加,所以不需要等待添加任务完成,因此能够输出 end
// 里层 用同步添加任务到主线程队列,因此又回到了死锁 1 ,因此程序卡死,不能输出 sync2
// 结论:永远也不要在主线程中添加事件
NSLog ( @"begin" );
dispatch_async(queue, ^{
dispatch_sync(queue, ^{
NSLog( @"sync2" );
});
});
NSLog ( @"end" );
// 造成死锁 1
// 在主线程队列中添加了一个任务,因为是同步,所以需要等待任务执行完了才能继续往下走
// 但是新添加的任务要想执行,由于放在队列的末尾,必须要等到添加的操作执行完了才能继续执行
// 因此,程序卡死,不能输出 end
NSLog ( @"begin" );
dispatch_sync(queue, ^{
NSLog( @"sync" );
});
NSLog ( @"end" );
// 解决方案改为 async
[[ NSRunLoop currentRunLoop ] run ];
// 死锁 2
// 外层 在主线程中添加一个任务,因为是异步添加,所以不需要等待添加任务完成,因此能够输出 end
// 里层 用同步添加任务到主线程队列,因此又回到了死锁 1 ,因此程序卡死,不能输出 sync2
// 结论:永远也不要在主线程中添加事件
NSLog ( @"begin" );
dispatch_async(queue, ^{
dispatch_sync(queue, ^{
NSLog( @"sync2" );
});
});
NSLog ( @"end" );
[[NSRunLoop currentRunLoop] run];
9. 在UIImageView中扩展一个方法,实现异步加载图片
- (
void
)downLoadImage:(
NSURL
*)url
{
dispatch_async ( dispatch_get_global_queue ( DISPATCH_QUEUE_PRIORITY_DEFAULT , 0 ), ^{
NSData *data = [ NSData dataWithContentsOfURL :url];
UIImage *image = [ UIImage imageWithData :data];
dispatch_async ( dispatch_get_main_queue (), ^{
self . image = image;
});
});
dispatch_async ( dispatch_get_global_queue ( DISPATCH_QUEUE_PRIORITY_DEFAULT , 0 ), ^{
NSData *data = [ NSData dataWithContentsOfURL :url];
UIImage *image = [ UIImage imageWithData :data];
dispatch_async ( dispatch_get_main_queue (), ^{
self . image = image;
});
});
}
10.GCD的延迟
//GCD
延迟调用
// 原理: dispatch_after 并不是在指定时间后执行任务处理,而是指定时间后将任务添加到队列中。所以会有少许的延迟
// 注意我们不能(直接)取消我们已经提交到 dispatch_after 里的代码
/*
void
dispatch_after(dispatch_time_t when,
dispatch_queue_t queue,
dispatch_block_t block);
*/
//dispatch_time_t->when 什么时候执行延迟调用的代码
//dispatch_queue_t -> queue 执行的任务放入到哪个队列
//dispatch_block_t -> block 具体执行的任务
// NSLog(@"hoho");
// 代码执行到此处的时间
CFAbsoluteTime first = CFAbsoluteTimeGetCurrent();
dispatch_time_t time = dispatch_time(DISPATCH_TIME_NOW, (int64_t)( 2 * NSEC_PER_SEC));
dispatch_after(time, dispatch_get_main_queue(), ^{
// NSLog(@"hehe");
NSLog( @"subtime is %f" , CFAbsoluteTimeGetCurrent() - first);
});
// 原理: dispatch_after 并不是在指定时间后执行任务处理,而是指定时间后将任务添加到队列中。所以会有少许的延迟
// 注意我们不能(直接)取消我们已经提交到 dispatch_after 里的代码
/*
void
dispatch_after(dispatch_time_t when,
dispatch_queue_t queue,
dispatch_block_t block);
*/
//dispatch_time_t->when 什么时候执行延迟调用的代码
//dispatch_queue_t -> queue 执行的任务放入到哪个队列
//dispatch_block_t -> block 具体执行的任务
// NSLog(@"hoho");
// 代码执行到此处的时间
CFAbsoluteTime first = CFAbsoluteTimeGetCurrent();
dispatch_time_t time = dispatch_time(DISPATCH_TIME_NOW, (int64_t)( 2 * NSEC_PER_SEC));
dispatch_after(time, dispatch_get_main_queue(), ^{
// NSLog(@"hehe");
NSLog( @"subtime is %f" , CFAbsoluteTimeGetCurrent() - first);
});
11.dispatch_group_async
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 ( @"block1" );
// sleep(4);
});
dispatch_group_async (group, queue, ^{
NSLog ( @"block2" );
});
dispatch_group_async (group, queue, ^{
NSLog ( @"block3" );
});
/*
汇总结果(两种方法二选一)
*/
//1. 所有任务执行结束汇总,不阻塞当前线程
// dispatch_group_notify(group, dispatch_get_main_queue(), ^{
// NSLog(@"group end");
// });
//2. 永久等待,直到所有任务执行结束,中途不能取消,阻塞当前线程
// dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
//2.1 扩展:等待 1 秒后汇总结果,不管任务有没有执行完
dispatch_time_t time = dispatch_time ( DISPATCH_TIME_NOW , ( int64_t )( 1 * NSEC_PER_SEC ));
long result = dispatch_group_wait (group, time);
if (result == 0 ) {
// 任务全部执行完成
NSLog ( @" 任务全部执行完成 " );
} else {
// 还有部分任务没有执行完成
NSLog ( @" 还有部分任务没有执行完成 " );
}
NSLog ( @"end" );
[[ NSRunLoop currentRunLoop ] run ];
dispatch_group_t group = dispatch_group_create ();
// 并发任务,执行顺序是随机的
dispatch_group_async (group, queue, ^{
NSLog ( @"block1" );
// sleep(4);
});
dispatch_group_async (group, queue, ^{
NSLog ( @"block2" );
});
dispatch_group_async (group, queue, ^{
NSLog ( @"block3" );
});
/*
汇总结果(两种方法二选一)
*/
//1. 所有任务执行结束汇总,不阻塞当前线程
// dispatch_group_notify(group, dispatch_get_main_queue(), ^{
// NSLog(@"group end");
// });
//2. 永久等待,直到所有任务执行结束,中途不能取消,阻塞当前线程
// dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
//2.1 扩展:等待 1 秒后汇总结果,不管任务有没有执行完
dispatch_time_t time = dispatch_time ( DISPATCH_TIME_NOW , ( int64_t )( 1 * NSEC_PER_SEC ));
long result = dispatch_group_wait (group, time);
if (result == 0 ) {
// 任务全部执行完成
NSLog ( @" 任务全部执行完成 " );
} else {
// 还有部分任务没有执行完成
NSLog ( @" 还有部分任务没有执行完成 " );
}
NSLog ( @"end" );
[[ NSRunLoop currentRunLoop ] run ];
}
12.使用Concurrent Dispatch Queue和dispatch_barrier_async函数可实现高效率的数据库访问和文件访问
dispatch_barrier_async的处理流程
13.信号量
dispatch_queue_t
queue =
dispatch_get_global_queue
(
DISPATCH_QUEUE_PRIORITY_DEFAULT
,
0
);
NSMutableArray *marr = [[ NSMutableArray alloc ] init ];
// 创建一个信号量 , 初始计数为 1, 初始计数的值取决于当前的并发数
dispatch_semaphore_t semaphore = dispatch_semaphore_create ( 1 );
for ( int i = 0 ; i < 100000 ; i++) {
dispatch_async (queue, ^{
//semaphore - 1, 如果 semaphore < 1 则等待,直到 semaphore 的计数值 >=1
dispatch_semaphore_wait (semaphore, DISPATCH_TIME_FOREVER );
[marr addObject : @( i ) ];
// 发信号,使得原来的信号计数值 +1
dispatch_semaphore_signal (semaphore);
});
}
// NSLog(@"marr.count is %ld",marr.count);// 在主线程中打印,可能不太准确
// 使用延迟 , 验证数组中添加了 100000 个数
dispatch_after ( dispatch_time ( DISPATCH_TIME_NOW , ( 4 * NSEC_PER_SEC )), queue, ^{
NSLog ( @"marr.count is %ld" ,marr. count );
NSSet *set = [ NSSet setWithArray :marr];
NSLog ( @"set.count is %ld" ,set. count );
});
NSMutableArray *marr = [[ NSMutableArray alloc ] init ];
// 创建一个信号量 , 初始计数为 1, 初始计数的值取决于当前的并发数
dispatch_semaphore_t semaphore = dispatch_semaphore_create ( 1 );
for ( int i = 0 ; i < 100000 ; i++) {
dispatch_async (queue, ^{
//semaphore - 1, 如果 semaphore < 1 则等待,直到 semaphore 的计数值 >=1
dispatch_semaphore_wait (semaphore, DISPATCH_TIME_FOREVER );
[marr addObject : @( i ) ];
// 发信号,使得原来的信号计数值 +1
dispatch_semaphore_signal (semaphore);
});
}
// NSLog(@"marr.count is %ld",marr.count);// 在主线程中打印,可能不太准确
// 使用延迟 , 验证数组中添加了 100000 个数
dispatch_after ( dispatch_time ( DISPATCH_TIME_NOW , ( 4 * NSEC_PER_SEC )), queue, ^{
NSLog ( @"marr.count is %ld" ,marr. count );
NSSet *set = [ NSSet setWithArray :marr];
NSLog ( @"set.count is %ld" ,set. count );
});
[[NSRunLoop currentRunLoop] run];
14.GCD控制多线程并发
/*
创建了一个初始值为 3 的信号量,每一次 for 循环都会创建一个新的线程,线程结束的时候会发送一个信号,线程创建之前会信号等待,所以当同时创建了 3 个线程之后, for 循环就会阻塞,等待,有线程结束之后会增加一个信号才能继续执行,如此就形成了对并发的控制
*/
dispatch_group_t group = dispatch_group_create ();
// 创建一个并发数为 3 的线程队列
dispatch_semaphore_t semaphore = dispatch_semaphore_create ( 3 );
dispatch_queue_t queue = dispatch_get_global_queue ( DISPATCH_QUEUE_PRIORITY_DEFAULT , 0 );
for ( int i = 0 ; i < 20 ; i++) {
dispatch_semaphore_wait (semaphore, DISPATCH_TIME_FOREVER );
dispatch_group_async (group, queue, ^{
NSLog ( @"%d" ,i);
sleep ( 2 );
dispatch_semaphore_signal (semaphore);
});
}
dispatch_group_wait (group, DISPATCH_TIME_FOREVER );
创建了一个初始值为 3 的信号量,每一次 for 循环都会创建一个新的线程,线程结束的时候会发送一个信号,线程创建之前会信号等待,所以当同时创建了 3 个线程之后, for 循环就会阻塞,等待,有线程结束之后会增加一个信号才能继续执行,如此就形成了对并发的控制
*/
dispatch_group_t group = dispatch_group_create ();
// 创建一个并发数为 3 的线程队列
dispatch_semaphore_t semaphore = dispatch_semaphore_create ( 3 );
dispatch_queue_t queue = dispatch_get_global_queue ( DISPATCH_QUEUE_PRIORITY_DEFAULT , 0 );
for ( int i = 0 ; i < 20 ; i++) {
dispatch_semaphore_wait (semaphore, DISPATCH_TIME_FOREVER );
dispatch_group_async (group, queue, ^{
NSLog ( @"%d" ,i);
sleep ( 2 );
dispatch_semaphore_signal (semaphore);
});
}
dispatch_group_wait (group, DISPATCH_TIME_FOREVER );
NSLog(@"end");