【问题标题】:How to wrap an asynchronous class to make it synchronous? Use NSRunLoop?如何包装异步类以使其同步?使用 NSRunLoop?
【发布时间】:2011-12-16 23:28:51
【问题描述】:

我目前正在开发一个 iPhone 应用程序,并且我有一个来自第三方的库,它具有异步行为,但我想用我自己的类进行包装并使其看起来是同步的。

这个库中的中心类,我们称它为 Connection 类,它有几个函数,当调用委托类的实例上的方法时,它们的最终结果会被解析。我想要做的是包装这个类和委托,使它看起来是同步的而不是异步的。如果我在 Java 中执行此操作,我会使用 FutureTask 或 CountdownLatch 或只是 join()。但我不确定在 Objective C 中执行此操作的最佳方法。

我首先创建了一个符合上述委托协议的 NSThread 扩展 NFCThread。这个想法是我将初始化和 NFCThread,将 NFCThread 实例传递给 Connection 的 setDelegate 方法,启动线程,然后在 Connection 上调用异步方法。我的期望是调用 NFCThread 实例上的三个委托方法之一,最终导致线程退出。

为了模拟连接,我做了以下操作。我在 NFCThread 中添加了一个 NSConditionalLock:

joinLock = [[NSConditionLock alloc] initWithCondition:NO];

调用 Connection 的代码如下所示:

NFCThread *t = [[NFCThread alloc] init];
[connection setDelegate:t];
[t start];

[connection openSession];
// Process errors, etc...

[t.joinLock lockWhenCondition:YES];
[t.joinLock unlock];
[t release];
[connection setDelegate:nil];

委托的协议具有三种方法。在 NFCThread 中,我实现了每个方法,如下所示:

- (void)didReceiveMessage:(CommandType)cmdType 
                     data:(NSString *)responseData 
               length:(NSInteger)length {
    NSLog(@"didReceiveMessage");
    // Do something with data and cmdType...
    [joinLock lock];
    [joinLock unlockWithCondition:YES];
    callBackInvoked = YES;
}

我重载了 NFCThread 的 main 方法,使它不断循环。像这样的:

while (!callBackInvoked) { ; }

我发现这并不是一个好主意,因为它会导致 CPU 使用率飙升。因此,我尝试使用我在此站点上找到的一些示例中的运行循环:

NSRunLoop *runLoop = [NSRunLoop currentRunLoop];
[runLoop addPort:[NSMachPort port] forMode:NSDefaultRunLoopMode];

while (!callBackInvoked) {
    [runLoop runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]];
}

在我的两个实现中,主线程总是被阻塞,并且似乎没有调用任何委托方法。但是,我知道该库运行正常,并且通常会调用委托方法。

我觉得我在这里遗漏了一些明显的东西。非常感谢任何帮助。

丰富

【问题讨论】:

    标签: objective-c multithreading nsrunloop


    【解决方案1】:

    您需要一个信号量,这将允许您的主代码路径阻塞,直到您的异步回调发出信号量并允许它继续。

    信号量可通过 Grand Central Dispatch 在 iOS 4 中使用。

    看来信号量的行为可以在 iOS 3 中通过 NSCondition 实现。

    【讨论】:

    • 感谢 Seamus,但不幸的是,我在这个问题中提到的库被编译为 3.x,所以,afaik,我们不能使用 GCD。还有其他建议吗?
    【解决方案2】:

    我刚刚实现了类似的东西。上下文是: - 在后台线程中包装对 ZipArchive 的多个调用 - 对于每次解压缩调用,显示不同的进度表(无限旋转的轮子,带有正在展开的文件的名称) - 当所有档案都展开后执行清理任务

    事实证明 NSConditionLock 让它变得简单,代码如下:

    NSConditionLock* lock = alloc/init
    for(int idx = 0; idx < [list count]; idx++) {
      NSString* fileName = ["getIdx's element in list"]
      [cond lockWhenCondition:idx]; //
    
      ... prepare things before forking
    
      [cond unlockWithCondition:-1];
      [Notification forkBlock:^(void){
        [cond lockWhenCondition:-1];
    
        NSAutoreleasePool *pool = [alloc/init];
        [ZipArchive unzipFile:fileName];
        [pool release];
        [cond unlockWithCondition:idx+1];
      }];
    }
    
    [cond lockWhenCondition:[list count];
    // all the tasks are now completed
    

    每个块都安排在后台线程中。 Notification 类负责使用旋转轮为 UIView 设置动画,并将块包装在另一个线程中。必须从同一个线程调用锁定/解锁,因此条件是启用从线程和后台线程之间的乒乓球(-1 表示后台,1,2,3..n 表示前台)。

    【讨论】:

      【解决方案3】:

      好的,这里有几个不同的问题,我正在考虑从哪里开始。

      但是为了让我们了解您要完成的工作,当您说您希望调用“出现”同步时,您的意思是您希望调用阻塞吗?您是从主线程进行此调用吗?如果是这样,那么您似乎在设计上阻塞了主线程。

      请记住,第三方库可能正在主运行循环上调度事件。您可以创建自己的运行循环并在另一个线程中运行它,但是您是否告诉其他库使用该运行循环来处理其事件? (例如,它可能会发出异步网络请求,这些请求被安排在您已阻止的主运行循环上)

      我会重新考虑一下您在做什么,但首先我们需要知道您是否打算阻止您进行此调用的线程。另外,你需要支持 iPhoneOS 3.x 吗?

      【讨论】:

      • 谢谢,Firoze。是的,我们的目的是让我们的同步调用阻塞。这个第三方库实际上是与 SD 卡接口,但我认为这只是一个细节。此外,经过仔细检查,我刚刚确定对委托方法的调用也发生在主线程上。这可能会使整个讨论变得毫无意义吗?
      • 好吧,我不确定它是否有实际意义。问题是如果你想阻塞调用,并且阻塞了主线程,那么这个其他库就无法完成它的工作。所以一个答案是从不同的线程进行同步调用,这将阻塞 that 线程,但仍允许主线程继续正常运行。当然,如果你最终以这种方式创建和阻塞了许多线程,那就不是很好了。在任何情况下,我都会考虑为什么你想要这种同步,并且可能会想到一种非阻塞方式来做同样的事情(需要了解更多你在这里尝试做的事情)
      • 我现在在不同的线程中调用阻塞方法,正如你建议的 Firoze,这似乎有效。有点。请参阅我对问题的关注:stackoverflow.com/questions/3444557/…
      【解决方案4】:

      您要做的是使用 NSCondition 等待当前线程,并在第二个线程上发出信号让第一个线程继续。

      - (void) firstThread
      {
          workIsDone = NO;
          //Start second thread here
          [condition lock];
          while (!workIsDone) {
              [condition wait];
          }
          [condition unlock];
      
          // Keep going
      }
      
      - (void) secondThread
      {
          [condition lock];
      
          //Do some work.
          workIsDone = YES;
          [condition signal];
          [condition unlock];
      }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2012-10-15
        • 2011-10-14
        • 2014-01-13
        • 2017-04-21
        • 1970-01-01
        • 2015-11-28
        • 2012-01-20
        • 1970-01-01
        相关资源
        最近更新 更多