문제

Maybe I mix too many different technologies together and run in some roadblock; some advise would be much appreciated.

I have an app which connects to several server; each connection with one input and output socket stream. The connection goes to a defined port and is close to telnet protocol. Text input/output. quite simple.

First I have an openStream function as wrapper called from main thread which create a client-specific GDC queue and dispatch the input/output-stream creation within that queue asynchronously:

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];
  });
}

Code for the technical open of steams ... // // in 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];
}

I open both socket streams and link them to a runloop within the GCD-queue (assuming it will indirectly create a thread; not sure if that is always guaranteed).

Then via the delegate (a member function of my connection class) for the streams in Code:

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

I do what I have to do with in data flooding in. No issues until I stay in foreground. I close the streams when I go into background to release the resources.

Now with iOS 7 I want to enable background refresh for the streams. For that i don't close the streams anymore when moving into background and have the notification code in

Code:

- (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);

}

This is one of my variants of background processing; not working well. This one supposed to wait a second and check if the input stream has data copied. If thats the case the parser would be called and the method comes to and end; removing one item from the dispatch group created in the iOS7 background app notification.

I don't like the dispatch_after as it seems very brute; but without I run in an endless loop as the streams seems not be triggered at all times.

Code:

- (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
      });
   }
}

But somehow I don't get the refresh done. Sometimes the completionHandler finish without any update and sometime my dispatch group never finish.

So my question is mainly: 1) what is your suggestion to combine background app refresh with multiple streams in GCD queues. 2) does those runloops still be active when I trigger in background 3) are the GCD queues still active 4) should I better schedule in one runloop for all client connection in addition to one main runloop ?

Somehow I need to fresh thoughts on the way forward. TIA

도움이 되었습니까?

해결책

I have been working on a cross platform real time multiplayer game and ran into similar problems. The core issue I faced was with multiple threads competing for compute cycles and bottlenecks while trying to update common resources. I also had a bunch of scheduled threads that updated my game state.

I ended up switching to a single run loop for all my connections and background scheduled events. That helped in clearly identifying and properly isolating critical sections. This also simplified my control flow. I also saw an increase in performance since there were only 4 threads now that were competing for cycles instead of the earlier 20+

So for your Q4 I recommend that you switch to a single schedule. For Q1 again, A single combined process for app refresh will not only speed up your app but make it easier to manage and debug.

For Q2 and Q3 It's best if you tested it out on your code by using logs as there may be factors involved that are not apparent in the code you shared.

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top