iphone - Should I use NSOperationQueue and NSOperation instead of NSThread?
Asked Answered
K

5

7

I am facing a design problem of my app.


Basically, the followings are what I am going to do in my app.

A single task is like this:

  1. Read custom object from the underlying CoreData databse
  2. Download a json from a url
  3. Parse the json to update the custom object or create a new one (parsing may take 1 - 3 secs, big data)
  4. Analyse the custom object (some calculations will be involved, may take 1 - 5 sec)
  5. Save the custom object into CoreData database.

There may be a number of tasks being executed concurrently.

The steps within one task obviously are ordered (i.e., without step 2 downloading the json, step 3 cannot continue), but they also can be discrete. I mean, for example, task2's step 4 can be executed before task1's step 3 (if maybe task2's downloading is faster than task1's)

Tasks have priorities. User can start a task with higher priority so all the task's steps will be tried to be executed before all others.


I hope the UI can be responsive as much as possible.

So I was going to creating a NSThread with lowest priority.

I put a custom priority event queue in that thread. Every step of a task becomes an event (work unit). So, for example, step 1 downloading a json becomes an event. After downloading, the event generates another event for step 3 and be put into the queue. every event has its own priority set.


Now I see this article: Concurrency and Application Design. Apple suggests that we Move Away from Threads and use GCD or NSOperation.

I find that NSOperation match my draft design very much. But I have following questions:

  • In consideration of iPhone/iPad cpu cores, should I just use one NSOperationQueue or create multiple ones?
  • Will the NSOperationQueue or NSOperation be executed with lowest thread priority? Will the execution affect the UI response (I care because the steps involve computations)?
  • Can I generate a NSOpeartion from another one and put it to the queue? I don't see a queue property in NSOperation, how do I know the queue?
  • How do I cooperate NSOperationQueue with CoreData? Each time I access the CoreData, should I create a new context? Will that be expensive?
  • Each step of a task become a NSOperation, is this design correct?

Thanks

Karilla answered 5/9, 2012 at 14:30 Comment(1)
That's a lot of questions. Consider asking each one as a separate question. Only the last requires any information about your particular application.Millard
B
4

In consideration of iPhone/iPad cpu cores, should I just use one NSOperationQueue or create multiple ones?

Two (CPU, Network+I/O) or Three (CPU, Network, I/O) serial queues should work well for most cases, to keep the app responsive and your programs streaming work by what they are bound to. Of course, you may find another combination/formula works for your particular distribution of work.

Will the NSOperationQueue or NSOperation be executed with lowest thread priority? Will the execution affect the UI response (I care because the steps involve computations)?

Not by default. see -[NSOperation setThreadPriority:] if you want to reduce the priority.

Can I generate a NSOpeartion from another one and put it to the queue? I don't see a queue property in NSOperation, how do I know the queue?

Sure. If you use the serial approach I outlined, locating the correct queue is easy enough -- or you could use an ivar.

How do I cooperate NSOperationQueue with CoreData? Each time I access the CoreData, should I create a new context? Will that be expensive?

(no comment)

Each step of a task become a NSOperation, is this design correct?

Yes - dividing your queues to the resource it is bound to is a good idea.

Bybidder answered 5/9, 2012 at 14:51 Comment(0)
R
0
  • By the looks, NSOperationQueue is what you're after. You can set the number of concurrent operations to be run at the same time. If using multiple NSOperation, they will all run at the same time ... unless you handle a queue on your own, which will be the same as using NSOperationQueue

  • Thread priority ... I'm not sure what you mean, but in iOS, the UI drawing, events and user interaction are all run on the main thread. If you are running things on the background thread, the interface will still be responsive, no matter how complicated or cpu-heavy operations you are running

  • Generating and handling of operations you should do it on the main thread, as it won't take any time, you just run them in a background thread so that your main thread doesn't get locked

  • CoreData, I haven't worked much with it specifically, but so far every Core~ I've worked with it works perfectly on background threads, so it shouldn't be a problem

  • As far as design goes, it's just a point of view ... As for me, I would've gone with having one NSOperation per task, and have it handle all the steps. Maybe write callbacks whenever a step is finished if you want to give some feedback or continue with another download or something

Rycca answered 5/9, 2012 at 14:48 Comment(1)
Thanks for your answer. The reason for having one step as a NSOperation is that one task may take too much time. And if a user wants to prioritise a task, and at the same, another task is being executed, user can't see what he wants be done soon.Karilla
S
0

The affection of computation when multithreading is not going to be different just because you are using NSThread instead of NSOperation. However keep in mind that must current iOS devices are using dual core processors.

Some of the questions you have are not very specific. You may or may not want to use multiple NSOperationQueue. It all depends on how you want to approach it. if you have different NSOperation subclasses, or different NSBlockOperations, you can manage order of execution by using priorities, or you might want to have different queues for different types of operations (especially when working with serial queues). I personally prefer to use 1 operation queue when dealing with the same type of operation, and have a different operation queue when the operations are not related/dependable. This gives me the flexibility to cancel and stop the operations within a queue based on something happening (network dropping, app going to the background).

I have never found a good reason to add an operation based on something happening during the execution of a current operation. Should you need to do so, you can use NSOperationQueue's class method, currentQueue, which will give you the operation queue in which the current operation is operating.

If you are doing core data work using NSOperation, i would recommend to create a context for each particular operation. Make sure to initialize the context inside the main method, since this is where you are on the right thread of the NSOperation background execution.

You do not necessarily need to have one NSOperation object for each task. You can download the data and parse it inside the NSOperation. You can also do the data download abstractly and do the data manipulation of the content downloaded using the completion block property of NSOperation. This will allow you to use the same object to get the data, but have different data manipulation.

My recommendation would be to read the documentation for NSOperation, NSBlockOperation and NSOperationQueue. Check your current design to see how you can adapt these classes with your current project. I strongly suggest you to go the route of the NSOperation family instead of the NSThread family.

Good luck.

Screen answered 5/9, 2012 at 14:53 Comment(8)
Thanks for your answer. But I don't find currentQueue method for NSOperation.developer.apple.com/library/mac/#documentation/Cocoa/Reference/…Karilla
@JacksonTale it's +[NSOperationQueue currentQueue]Bybidder
Ah thank you, thought it is in NSOperation. I have checked the doc for NSOperationQueue.Karilla
Can I know whether creating a new context for each operation is too time or resource consuming?Karilla
Thank you @justin, I have edited my response with the proper class for the method. jackson, once you create your core data stack, NSManagedObjectContext objects are not very expensive. Since you aren't recreating the stack, performance won't be a problem. Before doing the implementation, read into the documentation for NSManagedObjectContext. You will need to merge the content changed by the NSManagedObjectContext on the background thread with the NSManagedObjectContext of the main thread in order to have the main NSManagedObjectContext updated with the saved background information.Screen
thanks @J2theC. One more question, if I put all read/write CoreData operation in a dedicated NSOperationQueue with maxConcurrentCount set to 1, can I just use one Context?Karilla
If you use an NSManagedObjectContext with a concurrency type of NSPrivateQueueConcurrencyType, you can, since you will be saving inside the block of the NSManagedObjectContext's private queue. I personally use NSConfinementConcurrencyType and create it inside the thread being used. I will soon do some testing using NSPrivateQueueConcurrencyType with its private queue to see how it performs, but haven't used it so far.Screen
Note that if you do not use NSConfinementConcurrencyType, the context will save using the current thread. Which means that you will need to create the content in the thread being used, which makes it impossible to be reused across NSOperation objects.Screen
S
0

Just to add to @justin's answer

How do I cooperate NSOperationQueue with CoreData? Each time I access the CoreData, should I create a new context? Will that be expensive?

You should be really careful when using NSOperation with Core Data.
What you always have to remember here is that if you want to run CoreData operations on a separate thread you have to create a new NSManagedObjectContext for that thread, and share the main's Managed Object Context persistant store coordinator (the "main" MOC is the one in the app delegate).

Also, it's very important that the new Managed Object Context for that thread is create from that thread. So if you plan to use Core Data with NSOperation make sure you initialize the new MOC in NSOperation's main method instead of init.

Here's a really good article about Core Data and threading

Sirotek answered 5/9, 2012 at 15:51 Comment(2)
Thanks. if I put all read/write CoreData operation in a dedicated NSOperationQueue with maxConcurrentCount set to 1, can I just use one Context? Can I assume if I set NSOperationQueue like that, it will be always running in one thread?Karilla
sadly no. NSOperationQueue manages it's own thread pool and it decides how to spawn threads according to the workload it has. so in short: no, there is no guarantee that it will always run on the same threadSirotek
L
-1

Use GCD - its a much better framework than NS*

Keep all your CoreData access on one queue and dispatch_async at the end of your routines to save back to your CoreData database.

If you have a developer account, check this WWDC video out: https://developer.apple.com/videos/wwdc/2012/?id=712

Larsen answered 5/9, 2012 at 15:34 Comment(12)
This is inaccurate. From the documentation: Note: In iOS 4 and later, operation queues use Grand Central Dispatch to execute operations. Prior to iOS 4, they create separate threads for non-concurrent operations and launch concurrent operations from the current thread. For a discussion of the difference between concurrent and non-concurrent operations and how they are executed, see NSOperation Class Reference.Screen
Its not inaccurate, it's just more modern and powerful - pre-iOS4 is much less relevant now, and post iOS4 Operation Queues have been 'ported' to GCD. Therefore if you're starting from scratch - you'll be better off understanding GCD than NSOperation.Larsen
I'm not sure that's true. NSOperationQueue is built on top of GCD (at least in newer versions of iOS) and it gives you some things for free that would be quite a lot of work using only GCD (dependencies for example). In general you should use the highest level framework that will do the job.Pops
Check the Apple videos, they recommend using GCD rather than anything else.Larsen
This is that you do not understand: NSOperationQueue is GCD. It's a higher lever framework that does lots of work for you. Apple recommends GCD over NSThread. NSOperationQueue is GCD.Screen
Look, I understand that - but what you haven't understood is that while NSOperationQueue has been built to use GCD now - it's more of a legacy improvement than a recommendation for the future. Which should someone invest in? In my company we exclusively use GCD over NSOperationQueue - it's easier to code, encourages good design patterns and easier to read - IMO. ARC Retain cycles suck big time, but you'd get bitten with NSBlockOperation just the same.Larsen
Can you provide evidence/documentation of what you call "legacy improvement"? I have never heard of such thing before with NSOperation/NSOperationQueue, so I am wondering how this works.Screen
If you watch the WWDC video, they describe a design pattern built around GCD with a 'Dispatch Queue per Subsystem'. This is really the way to do things, it's super simple, gives great performance and is easy to code. The recommendation for GCD over anything else, actually comes from a course at CMU... Check out itunesu.itunes.apple.com/WebObjects/LZDirectory.woa/ra/… - understanding GCD also helps people understand the behaviour of other Cocoa design patterns that have shifted to Blocks. NSOperation just improves the performance of legacy code for iOS4+.Larsen
But in general - "use the highest abstraction" would be the right answer.. I think GCD is just the 'exception' in this case.Larsen
I am sorry, but I meant documentation for the "legacy improvement" regarding the NSOperation/NSOperationQueue subclasses. The current apple videos for wwdc they are still recommending the use of NSOperationScreen
The legacy improvement is because they moved from NSThread to GCD as the implementation behind NSOperation, so anything that used NSOperation beforehand automatically benefited from GCD's lightweight model. Please look at the video I linked to (both of them in fact...) - the WWDC one from Kevin Van Vechtan is really good. The CMU one is also really useful. There's nothing wrong with NSOperationQueue per se, but I believe GCD to be easier to understand and more versatile. NSOperationQueue is fine if that's what you're used to already - but if you're learning from scratch, head straight for GCD.Larsen
I wouldn't call changing from NSThread to GDC a legacy improvement. This is a complete change of the implementation, only leaving the interface intact. GCD being easier to understand is debatable, and depends on the user's perspective. I happen to use both GCD and all the time, and i do not find a reason or information to say that GCD is really much better than NSOpeation, or why someone should go directly to GCD and not evaluate NSOperation, considering the fact that there are some things that NSOperation does that would be hard to do under GCD (like dependencies).Screen

© 2022 - 2024 — McMap. All rights reserved.