0

I define a property that returns a serial dispatch queue using lazy instantiation, something like this:

@property (nonatomic, readonly) dispatch_queue_t queue;

- (dispatch_queue_t)queue
{
    if (!_queue) {
        _queue = dispatch_queue_create("com.example.MyQueue", NULL);
    }
    return _queue;
}

Then let's say that I define an action method for some button that adds a block to the queue:

- (IBAction)buttonTapped:(UIButton *)sender
{
    dispatch_async(self.queue, ^{
        printf("Do some work here.\n");
    });
}

The code for the actual method is more involved than a simple print statement, but this will do for the example.

So far so good. However, if I build and run the program, I can tap on the button 10 times and see the block run, but when I tap an eleventh time, the program hangs.

If I change the serial queue to a concurrent queue, no problems. I can dispatch as many blocks to the queue as I like.

Any idea what might be going on? Is there a limit to the number of blocks that can be posted to a serial queue?

Steve
  • 154
  • 1
  • 4

2 Answers2

0

In answer to the question on max blocks, I know of no practical limitation on what can be queued (other than available memory). Certainly, you should be able to queue far more than ten without incident.

But you have a typo in your queue getter method. You're setting _queue, but returning queue. You should return the same variable that you set. It looks like you must have two ivars defined; perhaps one that you defined manually and one that was synthesized? If you have a manually declared instance variable, you should just eliminate it and make sure your getter method is using the same instance variable, namely the one that was synthesized for you. Also, are you initializing this ivar in your init method?

If fixing this doesn't resolve the issue, then the problem probably rests in the particular code that you're dispatching to this queue and you should share that with us. Any synchronization code there? Any interaction with any shared resources?

Rob
  • 415,655
  • 72
  • 787
  • 1,044
  • I changed `queue` to `_queue` in my sample code. As far as the actual code in the block, it's fairly innocuous. It does perform call `NSURLConnection` `sendSynchronousRequest`. It doesn't contain any other synchronization code, no calls to dispatch_async, etc. The thing I find most unusual is that the code fails on 10 and exactly 10 blocks being added to the queue. Could be a coincidence, but unusual and totally repeatable. – Steve Aug 28 '13 at 20:32
  • @user2460554 If you replace your `NSURLConnection` code with something like `sleep(5)` (sleep for five seconds, more than enough time for you to queue up more than ten requests) and a `NSLog`, I suspect it will be fine. If that works, you know the problem is a result of your code in that dispatch block. I could imagine either (a) some resource contention (you say you're not doing any synchronization, something that is generally important in multithreaded code); or (b) some problem where your server objects to too many requests within a period of time (e.g. a DDNS protection). – Rob Aug 28 '13 at 21:20
  • First of all, I appreciate your responses...I certainly could try what you suggest (and will, in fact, try it). However, that doesn't explain to me why I have a problem using a serial queue but not a concurrent queue. Using a concurrent queue, everything works just fine. Can you think of a reason why concurrent queues would work but not serial queues? – Steve Aug 28 '13 at 21:28
  • @user2460554 No, I'm hard-pressed to guess why concurrent succeeds, but serial fails (frankly the converse is usually the case) nor, furthermore, why it always fails after ten requests, never nine, never eleven, etc. I've definitely queued up far in excess of ten without incident, so I don't suspect any GCD limitation. If you do some trivial tests, I'd wager you're going to find that the problem is contingent upon the code inside your queued dispatch requests, not anything intrinsic in GCD itself. – Rob Aug 28 '13 at 21:53
0

OK, I finally resolved this issue.

First, when I reported that concurrent queues worked fine, but serial queues did not, I was mistaken. Both types of queues failed. When I observed everything working, that was actually using the main queue. So, in that case, there really wasn't any concurrency.

That said, the problem was a deadlock issue having to do with logging information via the main thread while I was processing on a secondary thread -- from either a serial or concurrent queue. To make matters worse, my app uses Lumberjack for logging which introduces additional threading issues. To resolve the matter, I wrapped every call to a Lumberjack logging method as follows:

dispatch_async(dispatch_get_main_queue(), ^{
    // do logging here
});

That took care of the problem. Thanks for the comments. They ultimately led me to a solution.

Steve
  • 154
  • 1
  • 4