上传图片是很常见的操作,一般的做法是把本地的file上传到后台换取网址,但是如果是同时上传多张图片时我们怎么知道什么时候全部的图片都换取完成了呢?
比较好想到的方法是递归式的上传,即在第一张图上传完成后递归调用上传第二张,依次类推,使用这种方法实现并发是非常困难的,如果只是单张依次上传,那么这种方法是可行的,但是未免会觉得有点不太舒服。
接下来我们说第二种方法,利用线程阻塞去控制上传
其原理很简单,用信号量来控制顺序
dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_async(queue, ^{
for (int i = 0; i < 3; i ++) {
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
NSLog(@"完成任务——%d",i);
dispatch_semaphore_signal(semaphore);
});
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
}
NSLog(@"任务全部执行完成");
});
semaphore 为信号量,通过信号量去控制任务的加载
主要通过两个方法:dispatch_semaphore_signal() 和 dispatch_semaphore_wait();
第一个方法会使信号量加1
第二个方法会使信号量减1
当信号量为0时,第二个方法会线程阻塞不再向下执行,直到信号量不为0
然后我们回头看一下代码,我们把循环中的任务拆开如下
dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_async(queue, ^{
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
NSLog(@"完成任务——1");
dispatch_semaphore_signal(semaphore);
});
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
NSLog(@"完成任务——2");
dispatch_semaphore_signal(semaphore);
});
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
NSLog(@"完成任务——3");
dispatch_semaphore_signal(semaphore);
});
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
NSLog(@"任务全部执行完成");
});
我们延迟两秒做为任务的执行时间
那么任务会最先执行到dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
由于我们开始创建的信号量为0,所以此时线程被阻塞,不会再向下执行任务2
当两秒以后,任务一执行到方法 dispatch_semaphore_signal(semaphore); 信号量加1,此时任务等待被唤醒,由于此时信号量为1,所以线程阻塞解除,程序向下执行到任务2,依次类推,就实现了任务一个一个按顺序完成的操作
有了这些方法,那么我们就可以把该方法封装到上传图片的网络请求中去,下面是我的封装方法,大家可以借鉴一下
//上传图片组
+ (dispatch_semaphore_t)imageToGroupUrl:(NSString *)url images:(NSArray <UIImage *>*)images
netType:(AppNetType)type
parameter:(NSDictionary *)parameter
pageInfo:(NSString *)page
uploadProgress:(void (^)(NSProgress *uploadProgress))progress
success:(void (^)(NSURLSessionDataTask *task, id responseObject))success
failure:(void (^)(NSURLSessionDataTask *task, NSError *error, id responseObject))failure
finish:(void (^)(void))finish {
__weak typeof(self) weakSelf = self;
dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_async(queue, ^{
__strong typeof(weakSelf) strongSelf = weakSelf;
for (UIImage *image in images) {
[strongSelf imageToUrl:url image:image netType:type parameter:parameter pageInfo:page uploadProgress:^(NSProgress *uploadProgress) {
if (progress) {
progress(uploadProgress);
}
} success:^(NSURLSessionDataTask *task, id responseObject) {
if (success) {
success(task, responseObject);
}
} failure:^(NSURLSessionDataTask *task, NSError *error, id responseObject) {
if (failure) {
failure(task, error, responseObject);
}
} finish:^(NSURLSessionDataTask *task, NSError *error, id responseObject) {
dispatch_semaphore_signal(semaphore);
}];
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
}
dispatch_async(dispatch_get_main_queue(), ^{
if (finish) {
finish();
}
});
});
return semaphore;
}
其中imageToUrl:image:netType:parameter:pageInfo:uploadProgress:success:failure:finish:方法是我封装上传图片的方法,使用AFNetWorking很简单就不贴出来了,大家换成自己的方法就行了
其中的block:
success为每张上传成功后的回调
finish 即为当所有图片全部上传成功后的回调
那么要是我们一开始创建的信号量不为0呢? 哈哈,这就是该方法比递归要高级一点的地方,很简单就不展开说了,大家自己研究吧,喜欢的朋友不要忘记点赞哦!
网友评论