You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

NSURLSessionDataTask配合dispatch_semaphore等待时任务阻塞不执行问题

问题根本原因

这是典型的队列死锁,核心逻辑链如下:

  • 你初始化NSURLSession时指定delegateQueue为[NSOperationQueue mainQueue],网络任务的启动调度、请求完成后的回调派发,全都会被提交到主队列,等待主线程空闲时才能执行。
  • 你在主线程调用dispatch_semaphore_wait会直接阻塞主线程,主队列中排队的所有任务(包括网络请求的发送逻辑、回调里的dispatch_semaphore_signal)都得不到执行机会,信号量永远等不到触发信号,就会永久卡住。
修复方案

方案1(不推荐,仅临时兼容现有同步逻辑)

如果必须保留信号量等待的写法,需要做两个必要修改:

  • 给NSURLSession分配独立的非主队列,不要依赖主队列派发任务和回调
  • 所有包含dispatch_semaphore_wait的请求逻辑必须放在非主线程执行,绝对不能阻塞主线程

修正后的核心代码:

// 给session创建独立工作队列,禁止使用mainQueue
NSURLSessionConfiguration *config = [NSURLSessionConfiguration defaultSessionConfiguration];
NSOperationQueue *sessionWorkQueue = [[NSOperationQueue alloc] init];
NSURLSession *session = [NSURLSession sessionWithConfiguration:config
                                                      delegate:nil
                                                 delegateQueue:sessionWorkQueue];

// 整个请求逻辑放到全局非主线程执行
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
    dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
    __block bool result = false;
    
    NSMutableURLRequest *request = [NSMutableURLRequest new];
    request.HTTPMethod = @"POST";
    NSString *urlString = @"http://localhost/api/test";
    [request setURL:[NSURL URLWithString:urlString]];
    [request setValue:@"application/json" forHTTPHeaderField:@"Content-Type"];
    [request setValue:@"application/json" forHTTPHeaderField:@"Accept"];
    // 建议直接用NSString转NSData,避免C数组栈溢出风险
    NSData *jsonBodyData = [@"reqtestmsg" dataUsingEncoding:NSUTF8StringEncoding];
    [request setHTTPBody:jsonBodyData];
    
    NSURLSessionDataTask *task = [session dataTaskWithRequest:request
                                            completionHandler:^(NSData * _Nullable data,
                                                                NSURLResponse * _Nullable response,
                                                                NSError * _Nullable error) {
        NSHTTPURLResponse *asHTTPResponse = (NSHTTPURLResponse *) response;
        NSLog(@"curl The response is: %@", asHTTPResponse);
        
        if (asHTTPResponse.statusCode == 200) {
            printf ("curl status 200 ok\n");
            result = true;
        }
        dispatch_semaphore_signal(semaphore);
    }];
    [task resume];
    dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
    // 拿到结果后如果要更新UI,必须切回主线程
    dispatch_async(dispatch_get_main_queue(), ^{
        // 处理result、更新UI逻辑
    });
});

方案2(推荐,符合框架设计逻辑)

NSURLSession本身就是为异步执行设计的,不要用信号量强行把异步请求转成同步,直接把结果处理逻辑放到请求的completionHandler回调里即可,从根源上消除线程阻塞、死锁的风险,也不会卡住UI响应。

NSURLSessionConfiguration *config = [NSURLSessionConfiguration defaultSessionConfiguration];
// 日常使用直接用主队列也没问题,因为不会阻塞主线程
NSURLSession *session = [NSURLSession sessionWithConfiguration:config
                                                      delegate:nil
                                                 delegateQueue:[NSOperationQueue mainQueue]];

NSMutableURLRequest *request = [NSMutableURLRequest new];
// 省略request配置逻辑,和原有代码一致

NSURLSessionDataTask *task = [session dataTaskWithRequest:request
                                        completionHandler:^(NSData * _Nullable data,
                                                            NSURLResponse * _Nullable response,
                                                            NSError * _Nullable error) {
    NSHTTPURLResponse *asHTTPResponse = (NSHTTPURLResponse *) response;
    NSLog(@"curl The response is: %@", asHTTPResponse);
    bool result = false;
    if (asHTTPResponse.statusCode == 200) {
        printf ("curl status 200 ok\n");
        result = true;
    }
    // 所有需要拿到result执行的逻辑直接写在这里
    // 如果是UI操作,确认当前在主线程执行即可
}];
[task resume];
// 不需要加任何等待逻辑,请求完成后回调会自动触发

额外提醒:原有代码中char json_string[20]的写法存在栈溢出风险,如果后续请求体长度超过19字节(需要预留C字符串结束符位置)会直接内存越界,建议直接通过NSString的dataUsingEncoding:方法生成请求体,不要手动操作C字符数组。

内容的提问来源于stack exchange,提问作者SongpaGu

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.28 08:27:20