leowang
leowang

Reputation: 33

how many operations NSOperationQueue can cache

I created an NSOperationQueue and set the maxConcurrentOperationCount property to 2. If I create 2 operations that do not stop, when I continue to add operations to it, the NSOperationQueue will cache these tasks, so what is the maximum number of operations that can be cached by the NSOperationQueue, will it cause a memory surge?

NSOperationQueue *queue = [[NSOperationQueue alloc] init];
queue.maxConcurrentOperationCount = 2;

[queue addOperationWithBlock:^{
    while (YES) {
        NSLog(@"thread1 : %@",[NSThread currentThread]);
    }
}];

[queue addOperationWithBlock:^{
    while (YES) {
        NSLog(@"thread2 : %@",[NSThread currentThread]);
    }
}];
  
// this operation will wait 
[queue addOperationWithBlock:^{
    while (YES) {
        NSLog(@"thread3 : %@",[NSThread currentThread]);
    }
}];

above is my code, third operation will never run, from what I understand, the queue will save these tasks, if you keep adding operations to it, the memory will keep going up, Will NSOperationQueue handle this situation internally?

Upvotes: 1

Views: 441

Answers (1)

Rob
Rob

Reputation: 437917

An operation queue will handle large number of operations without incident. It is one of the reasons we use operation queues, to gracefully handle constrained concurrency for a number of operations that exceed the maxConcurrentOperationCount.

Obviously, your particular example, with operations spinning indefinitely, is both inefficient (tying up two worker threads with computationally intensive process) and will prevent the third operation from ever starting. But if you changed the operations to something more practical (e.g., ones that finish in some finite period of time), the operation queue can gracefully handle a very large number of operations.

That is not to say that operation queues can be used recklessly. For example, one can easily create operation queue scenarios that suffer from thread explosion and exhaust the limited worker thread pool. Or if you had operations that individually used tons of memory, then eventually, if you had enough of those queued up, you could theoretically introduce memory issues.

But don’t worry about theoretical problems. If you have a practical example for which you are having a problem, then post that as a separate question. But, in answer to your question here, operation queues can handle many queued operation quite well.


Let us consider queuing 100,000 operations, each taking one second to finish:

NSOperationQueue *queue = [[NSOperationQueue alloc] init];
queue.maxConcurrentOperationCount = 2;

for (NSInteger i = 0; i < 100000; i++) {
    [queue addOperationWithBlock:^{
        [NSThread sleepForTimeInterval:1];
        NSLog(@"%ld", i);
    }];
}

NSLog(@"done queuing");

The operation queue handles all of these operations, only running two at a time, perfectly well. It will take some memory (e.g. 80mb) to hold these 100,000 operations in memory at a given moment, but it handles it fine. Even at 1,000,000 operations, it works fine (but will take ~500mb of memory). Clearly, at some point you will run out of memory, but if you're thinking of something with this many operations, you should be considering other patterns, anyway.


There are obviously practical limitations.

Let us consider a degenerate example: Imagine you had a multi-gigabyte video file and you wanted to run some task on each frame of the video. It would be a poor design to add operations for each frame, up-front, passing it the contents of the relevant frame of the video (because you would effectively be trying to hold the entire video, frame-by-frame, in memory at one time).

But this is not an operation queue limitation. This is just a practical memory limitation. We would generally consider a different pattern. In my hypothetical example, I would consider dispatch_apply, known as concurrentPerform in Swift, that would simply load the relevant frame in a just-in-time manner.

Bottom line, just consider how much memory will be added for each operation added to the queue and use your own good judgment as to whether holding all of these in memory at the same time or not. If you have many operations, each holding large piece of data in memory, then consider other patterns.

Upvotes: 2

Related Questions