Nsoperation and Nsoperationqueue Working Thread VS Main Thread

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



Leave a reply



Submit