带有runloops的GCD和应用程序iOS7的后台刷新

时间:2014-03-19 23:20:25

标签: multithreading ios7 background grand-central-dispatch

也许我将太多不同的技术混合在一起并在一些障碍中运行;一些建议将不胜感激。

我有一个连接到多个服务器的应用程序;每个连接有一个输入和输出套接字流。连接转到定义的端口,并且接近telnet协议。文字输入/输出。非常简单。

首先,我有一个openStream函数作为从主线程调用的包装器,它创建一个特定于客户端的GDC队列,并异步地在该队列中调度输入/输出流:

gcdQueue = dispatch_queue_create([self.client.hostName cStringUsingEncoding:NSASCIIStringEncoding], DISPATCH_QUEUE_CONCURRENT);

// possible priorities:
// DISPATCH_QUEUE_PRIORITY_HIGH
// DISPATCH_QUEUE_PRIORITY_DEFAULT
// DISPATCH_QUEUE_PRIORITY_LOW

if (self.runASync)
{
  //      dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0), ^{
  dispatch_async(gcdQueue, ^{
     [self openStreamsInternal];
  });
}

蒸汽技术开放代码     ...      //      //在openStreamsInternal()中      //

CFStreamCreatePairWithSocketToHost(NULL, (__bridge CFStringRef)self.client.hostName, [self.client.hostPort intValue], &_readStream, &_writeStream);

  self.inputStream = (__bridge_transfer NSInputStream *)_readStream;
  self.outputStream = (__bridge_transfer NSOutputStream *)_writeStream;

  [self.inputStream setDelegate:self];
  [self.outputStream setDelegate:self];

  self.runLoop = [NSRunLoop currentRunLoop];

  [self.inputStream scheduleInRunLoop:self.runLoop forMode:NSDefaultRunLoopMode];
  [self.outputStream scheduleInRunLoop:self.runLoop forMode:NSDefaultRunLoopMode];

  [self.inputStream open];
  [self.outputStream open];

 // ... some lines later

if (self.runASync && (self.inputStream || self.outputStream))
{
   [self.runLoop run];
}

我打开两个套接字流并将它们链接到GCD队列中的runloop(假设它将间接创建一个线程;不确定是否始终保证)。

然后通过委托(我的连接类的成员函数)为流中的 代码:

- (void)stream:(NSStream *)stream handleEvent:(NSStreamEvent)eventCode

我在数据泛滥中做了我必须做的事情。在我留在前台之前没有问题。当我进入后台以释放资源时,我关闭了流。

现在使用iOS 7我想为流启用后台刷新。为此,当移动到后台并且在

中有通知代码时,我不再关闭流

代码:

- (void)application:(UIApplication *)application performFetchWithCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler
{

   NSLog(@"called in background for data fetch");


   dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
   dispatch_group_t group = dispatch_group_create();


   for (Connection *connection in self.document.clientList)
   {
      // Add a task to the group
      [connection parseResponseInQueue:group];
   }

   dispatch_group_wait(group, DISPATCH_TIME_FOREVER);

   NSLog(@"finished with background for data fetch");
   completionHandler(UIBackgroundFetchResultNewData);

}

这是我的后台处理变体之一;不太好用。这个应该等待一秒钟并检查输入流是否已复制数据。如果是这样的话,将调用解析器并且方法结束;从iOS7后台应用程序通知中创建的调度组中删除一个项目。

我不喜欢dispatch_after,因为它看起来很粗野;但是没有我在无限循环中运行,因为流似乎不会一直被触发。

代码:

- (void)parseResponseInQueue:(dispatch_group_t)group
{
   if (gcdQueue != nil)
    {
      dispatch_group_async(group, gcdQueue, ^{

         while ([self.data length] > 0)
         {
            dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 1 * NSEC_PER_SEC), gcdQueue, ^{
               NSLog(@"%@, wait for parser in background", self.client.hostName);
            });
         }
 #if 0
         if ([self.data length] > 0)
         {
            NSLog(@"%@, start working on buffer %d from background", self.client.hostName, [self.data length]);
            [self parseResponse];

             NSLog(@"%@, finish working on buffer, left %d in background", self.client.hostName, [self.data length]);
            //                  NSLog(@"data   : %@", [[NSString alloc] initWithData:self.data encoding:NSUTF8StringEncoding]);
         }
         else
         {
             NSLog(@"%@, no data for background processing", self.client.hostName);
         }
  #endif
      });
   }
}

但不知怎的,我没有完成刷新。有时,completionHandler完成没有任何更新,有时我的调度组永远不会完成。

所以我的问题主要是: 1)您的建议是将后台应用程序刷新与GCD队列中的多个流相结合。 2)当我在后台触发时,那些runloops是否仍处于活动状态 3)GCD队列仍处于活动状态 4)除了一个主要的runloop之外,我应该更好地在一个runloop中安排所有客户端连接吗?

不知怎的,我需要对前进的方向有新的想法。 TIA

1 个答案:

答案 0 :(得分:0)

我一直致力于跨平台实时多人游戏并遇到类似问题。我遇到的核心问题是多个线程在尝试更新公共资源时竞争计算周期和瓶颈。我还有一堆预定的线程来更新我的游戏状态。

我最终切换到所有连接和后台预定事件的单个运行循环。这有助于明确识别和正确隔离关键部分。这也简化了我的控制流程。我也看到了性能的提升,因为现在只有4个线程竞争周期而不是早期的20 +

因此,对于您的Q4,我建议您切换到单个时间表。 再次针对Q1,应用程序刷新的单个组合流程不仅可以加速您的应用程序,还可以更轻松地进行管理和调试。

对于Q2和Q3如果您使用日志对代码进行了测试,那将是最好的,因为可能涉及的因素在您共享的代码中并不明显。