NSOperation and NSOperationQueue working thread vs main thread
My question is whether the database write operation occur in main
thread or in a background thread?
If you create an NSOperationQueue
from scratch as in:
NSOperationQueue *myQueue = [[NSOperationQueue alloc] init];
It will be in a background thread:
Operation queues usually provide the threads used to run their
operations. In OS X v10.6 and later, operation queues use the
libdispatch library (also known as Grand Central Dispatch) to initiate
the execution of their operations. As a result, operations are always
executed on a separate thread, regardless of whether they are
designated as concurrent or non-concurrent operations
Unless you are using the mainQueue
:
NSOperationQueue *mainQueue = [NSOperationQueue mainQueue];
You can also see code like this:
NSOperationQueue *myQueue = [[NSOperationQueue alloc] init];
[myQueue addOperationWithBlock:^{
// Background work
[[NSOperationQueue mainQueue] addOperationWithBlock:^{
// Main thread work (UI usually)
}];
}];
And the GCD version:
dispatch_async(dispatch_get_global_queue( DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^(void)
{
// Background work
dispatch_async(dispatch_get_main_queue(), ^(void)
{
// Main thread work (UI usually)
});
});
NSOperationQueue
gives finer control with what you want to do. You can create dependencies between the two operations (download and save to database). To pass the data between one block and the other, you can assume for example, that a NSData
will be coming from the server so:
__block NSData *dataFromServer = nil;
NSBlockOperation *downloadOperation = [[NSBlockOperation alloc] init];
__weak NSBlockOperation *weakDownloadOperation = downloadOperation;
[weakDownloadOperation addExecutionBlock:^{
// Download your stuff
// Finally put it on the right place:
dataFromServer = ....
}];
NSBlockOperation *saveToDataBaseOperation = [[NSBlockOperation alloc] init];
__weak NSBlockOperation *weakSaveToDataBaseOperation = saveToDataBaseOperation;
[weakSaveToDataBaseOperation addExecutionBlock:^{
// Work with your NSData instance
// Save your stuff
}];
[saveToDataBaseOperation addDependency:downloadOperation];
[myQueue addOperation:saveToDataBaseOperation];
[myQueue addOperation:downloadOperation];
Edit: Why I am using __weak
reference for the Operations, can be found here. But in a nutshell is to avoid retain cycles.
NSOperationQueue never on main thread?
Correct, operation queues (other than the main operation queue returned by +[NSOperationQueue mainQueue]
) are never serviced by the main thread. From the NSOperationQueue
class reference:
Operation queues usually provide the threads used to run their
operations. In OS X v10.6 and later, operation queues use the
libdispatch library (also known as Grand Central Dispatch) to initiate
the execution of their operations. As a result, operations are always
executed on a separate thread, regardless of whether they are
designated as concurrent or non-concurrent operations.
NSOperation running on main thread
The issue is that Alamofire dispatches its completion handlers to the main queue (the theory being that they want to make it easy for you to update the UI and/or model).
If you want to use a different queue for the completion handlers, supply the queue parameter to the response
or responseJSON
methods.
For example, have some property:
var completionHandlerQueue = dispatch_queue_create("com.domain.completionhandler", nil)
And then, when you perform the request:
Alamofire.request(.GET, URLString).responseJSON(queue: completionHandlerQueue) { response in
guard let test = response.result.value else {
print("no data")
print(response.result.error)
return
}
// do something with `test` here
print(test)
NSLog("main thread? %@", NSThread.isMainThread() ? "YES" : "NO");
}
This raises the question of whether you really care if the completion handlers are called on the main queue or not. Sure, if you're doing something computationally intensive there, then, by all means, specify a different queue. But there's no reason why an operation can't perform some of its tasks on the main queue. In fact, it's awfully useful at times to just have the completion handlers run on the main queue, saving you from needing to synchronize your model updates and/or dispatch your UI updates to the main queue.
To step back even further, we have to ask what the intent of using NSOperation
is. If the goal is merely to enjoy asynchronous processing of requests, NSOperation
is not needed. But if (a) you're using NSOperation
to manage dependencies of different operations; and (b) you're not using background sessions, then, by all means, wrap your requests in operations.
Run NSOperation on Main Thread
You can do this in the NSOperationQueue
by using mainQueue
http://developer.apple.com/library/mac/#documentation/Cocoa/Reference/NSOperationQueue_class/Reference/Reference.html
NSOperationQueue, concurrent operation and thread
I think you might be misunderstanding the meaning of isConcurrent
. This is totally understandable, since it's incredibly poorly named. When you return YES
from -isConcurrent
what it really means is "I will handle any concurrency needs associated with this operation, and will otherwise behave asynchronously." In this situation, NSOperationQueue
is free to call -start
on your operation synchronously on the thread from which you add the operation. The NSOperationQueue
is expecting that, because you've declared that you will manage your own concurrency, that your -start
method will simply kick off an asynchronous process and return right away. I suspect this is the source of your problem.
If you implemented your operation by overriding -main
then you almost certainly want to be returning NO
from isConcurrent
. To make matters more complicated, the behaviors associated with isConcurrent
have changed over the years (but all that is covered in the official docs.) A fantastic explanation of how to properly implement a returns-YES
-from-isConcurrent
NSOperation
can be found here.
The way I'm reading your question here, it doesn't sound like your NSOperation
subclass actually needs to "manage its own concurrency", but rather that you simply want it to execute asynchronously, "on a background thread", potentially "concurrently" with other operations.
In terms of assuring the best performance for your AVCaptureVideoDataOutputSampleBufferDelegate
callbacks, I would suggest making the queue you pass in to -setSampleBufferDelegate:queue:
be (itself) concurrent and that it target the high-priority global concurrent queue, like this:
dispatch_queue_t queue = dispatch_queue_create("it.CloudInTouchLabs.avsession", DISPATCH_QUEUE_CONCURRENT);
dispatch_set_target_queue(queue, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0));
[dataOutput setSampleBufferDelegate:(id)self queue:queue];
Then you should make the delegate callback methods as lightweight as possible -- nothing more than packaging up the information you need to make the NSOperation
and add it to the NSOperationQueue
.
This should ensure that the callbacks always take precedence over the NSOperations
. (It's my understanding that NSOperationQueue
targets either the main queue (for the NSOperationQueue
that's associated with the main thread and run loop) or the default priority background queue.) This should allow your callbacks to fully keep up with the frame rate.
Another important thing to take away here (that another commenter was getting at) is that if you're doing all your concurrency using GCD, then there is only one special thread -- the main thread. Other than that, threads are just a generic resource that GCD can (and will) use interchangeably with one another. The fact that a thread with a thread ID of X was used at one point to service your delegate callback, and at another point was used to do your processing is not, in and of itself, indicative of a problem.
Hope this helps!
NSOperation is not happening in background thread
No matter what, if I override -start or -main this block of code always happens:
The main operation queue runs on the main thread. From the docs for +[NSOperationQueue mainQueue]
:
The returned queue executes operations on the main thread. The main
thread’s run loop controls the execution times of these operations.
So, running in another thread is a matter of what queue you add the operation to, not how you write the operation's code. If you want your operation to run on a different operation queue, you'll need to create a queue of your own using
NSOperationQueue* aQueue = [[NSOperationQueue alloc] init];
You can find an example in Adding Operations to an Operation Queue in the Concurrency Programming Guide.
But this causes a crash, an EXC_BAD_ACCESS violation pointing at the dispatch_async line.
It sounds like -[NSOperation start]
probably isn't re-entrant. Your code effectively executes the same method on two different threads. In fact, look at the docs for -start
, it's obvious that your code won't work:
You can call this method explicitly if you want to execute your
operations manually. However, it is a programmer error to call this
method on an operation object that is already in an operation queue
or to queue the operation after calling this method. Once you add an
operation object to a queue, the queue assumes all responsibility for
it. [Emphasis added. -Caleb]
In other words, don't do that.
Related Topics
Formatting a Uitextfield for Credit Card Input Like (Xxxx Xxxx Xxxx Xxxx)
How to Simultaneously Satisfy Constraints - No Constraints in Place
Present and Dismiss Modal View Controller
Cannot Assign a Value of Type "String" to Type "Uilabel" in Swift
When Should I Use the Various Storage Mechanisms in iOS
Swift - Saving Highscore Using Nsuserdefaults
Remove Println() for Release Version iOS Swift
iOS 8 Auto Cell Height - Can't Scroll to Last Row
Nsdate Beginning of Day and End of Day
Gradient Polyline with Mapkit iOS
What Does "@Private" Mean in Objective-C
How to Access My Viewcontroller from My Appdelegate? iOS
Uiimage Imagenamed Returns Nil
Presentviewcontroller from Custom Tablecell in Xib
Restoring Animation Where It Left Off When App Resumes from Background
iPhone - Draw Transparent Rectangle on Uiview to Reveal View Beneath