前几天,同事多线程访问sqlite的时候遇到一个问题,我也跟着看了一下,这几天又刚好看了一些相关的博文,发现一个问题,所以在此记录一下。
先展示代码如下:
dispatch_queue_t q = dispatch_queue_create("queue", DISPATCH_QUEUE_SERIAL);
for (NSUInteger i = 0; i < 10; i++) {
}
以下是日志打印
2019-08-30 16:34:39.999006+0800 odinDomain[57322:1491164] current thread = <NSThread: 0x600000464a80>{number = 5, name = (null)}, index = 0
2019-08-30 16:34:39.999211+0800 odinDomain[57322:1491164] current thread = <NSThread: 0x600000464a80>{number = 5, name = (null)}, index = 1
2019-08-30 16:34:39.999977+0800 odinDomain[57322:1491164] current thread = <NSThread: 0x600000464a80>{number = 5, name = (null)}, index = 2
2019-08-30 16:34:40.000368+0800 odinDomain[57322:1491164] current thread = <NSThread: 0x600000464a80>{number = 5, name = (null)}, index = 3
2019-08-30 16:34:40.001670+0800 odinDomain[57322:1491164] current thread = <NSThread: 0x600000464a80>{number = 5, name = (null)}, index = 4
2019-08-30 16:34:40.001950+0800 odinDomain[57322:1491164] current thread = <NSThread: 0x600000464a80>{number = 5, name = (null)}, index = 5
2019-08-30 16:34:40.002119+0800 odinDomain[57322:1491164] current thread = <NSThread: 0x600000464a80>{number = 5, name = (null)}, index = 6
2019-08-30 16:34:40.002254+0800 odinDomain[57322:1491164] current thread = <NSThread: 0x600000464a80>{number = 5, name = (null)}, index = 7
2019-08-30 16:34:40.002762+0800 odinDomain[57322:1491164] current thread = <NSThread: 0x600000464a80>{number = 5, name = (null)}, index = 8
2019-08-30 16:34:40.002905+0800 odinDomain[57322:1491164] current thread = <NSThread: 0x600000464a80>{number = 5, name = (null)}, index = 9
代码是在主线程执行的,从日志可以看出,串行队列异步执行任务,会新建一个线程,然后串行队列的任务会在新建的线程里面,按照加入的顺序执行,这与我们预期的效果是一样的。
现在将代码改动 如下
dispatch_queue_t q = dispatch_queue_create("queue", DISPATCH_QUEUE_SERIAL);
for (NSUInteger i = 0; i < 10; i++) {
NSLog(@"start request index = %@", @(i));
[ODMHttp post:@"http://192.168.124.11:5000/odindomain" parameters:@{@"index": @(i)} success:^(id _Nullable responseObject) {
NSLog(@"callback thread = %@, index = %@", NSThread.currentThread, @(i));
dispatch_async(q, ^{
NSLog(@"async thread = %@, index = %@", NSThread.currentThread, @(i));
});
} failure:^(NSError * _Nullable error) {}];
}
打印的日志如下:
17:14:37.7034 callback thread = <NSThread: 0x600000279880>{number = 4, name = (null)}, index = 1
17:14:37.7046 async thread = <NSThread: 0x600000279880>{number = 4, name = (null)}, index = 1
17:14:37.7108 callback thread = <NSThread: 0x60000027cf00>{number = 5, name = (null)}, index = 2
17:14:37.7116 callback thread = <NSThread: 0x60000027cf00>{number = 5, name = (null)}, index = 0
17:14:37.7117 async thread = <NSThread: 0x600000279880>{number = 4, name = (null)}, index = 2
17:14:37.7127 callback thread = <NSThread: 0x60000027cf00>{number = 5, name = (null)}, index = 3
17:14:37.7128 async thread = <NSThread: 0x600000279880>{number = 4, name = (null)}, index = 0
17:14:37.7138 async thread = <NSThread: 0x600000279880>{number = 4, name = (null)}, index = 3
17:14:37.7457 callback thread = <NSThread: 0x604000460380>{number = 6, name = (null)}, index = 4
17:14:37.7461 async thread = <NSThread: 0x604000460380>{number = 6, name = (null)}, index = 4
17:14:37.7672 callback thread = <NSThread: 0x60000027cf00>{number = 5, name = (null)}, index = 5
17:14:37.7686 callback thread = <NSThread: 0x60000027cf00>{number = 5, name = (null)}, index = 6
17:14:37.7694 async thread = <NSThread: 0x600000279880>{number = 4, name = (null)}, index = 5
17:14:37.7745 async thread = <NSThread: 0x600000279880>{number = 4, name = (null)}, index = 6
17:14:37.7839 callback thread = <NSThread: 0x600000279880>{number = 4, name = (null)}, index = 7
17:14:37.7853 async thread = <NSThread: 0x60000027c300>{number = 7, name = (null)}, index = 7
17:14:37.7928 callback thread = <NSThread: 0x600000279880>{number = 4, name = (null)}, index = 8
17:14:37.7945 async thread = <NSThread: 0x604000460380>{number = 6, name = (null)}, index = 8
17:14:37.7960 callback thread = <NSThread: 0x60000027d8c0>{number = 8, name = (null)}, index = 9
17:14:37.7968 async thread = <NSThread: 0x60000027d8c0>{number = 8, name = (null)}, index = 9
由此可以发现问题,将任务添加到串行队列之后,串行队列在执行任务的时候,并不是在一个线程,从而引发另一个问题,这些任务的执行有两种情况,并发和串行。
因为是任务在不同的线程执行的,所以怀疑可能是并发的执行;
又因为是在串行队列中,所以也可能是串行执行,只不过是自不同线程的里面的执行的。
于是将代码改为如下所示:
@interface ViewController ()
@property (nonatomic, assign) NSInteger counts;
@end
- (void)viewDidLoad {
[super viewDidLoad];
self.counts = 1000;
}
- (void)testRequestQueue {
dispatch_queue_t q = dispatch_queue_create("queue", DISPATCH_QUEUE_SERIAL);
for (NSUInteger i = 0; i < 100; i++) {
NSLog(@"start request index = %@", @(i));
[ODMHttp post:@"http://192.168.124.11:5000/odindomain" parameters:@{@"index": @(i)} success:^(id _Nullable responseObject) {
NSLog(@"callback thread = %@, index = %@", NSThread.currentThread, @(i));
dispatch_async(q, ^{
self.counts -= 1;
NSLog(@"async thread = %@, index = %@ counts = %@", NSThread.currentThread, @(i), @(self.counts));
});
} failure:^(NSError * _Nullable error) {
}];
}
}
如果是多线程并发执行任务的话,那么counts打印的顺序应该会是乱序,经过我多次测试,counts的打印都是顺序的。
再者如果在打印count之前再添加一句代码
[NSThread sleepForTimeInterval:3];
那么会发现结果很有意思,所有的任务的执行又放在同一个线程里面,并且是串行执行的。
由以上的测试的结果,可以得出一个粗略的结论,串行队列可以保证队列里的任务是串行执行的,但是不能保证这些任务是在同一个线程里面执行 。这里面涉及到iOS对线程复用的知识,还未深究。