dispatch_queue and Thread and task order - ios

guys,little confused abt dispatch queue and thread,
there could be several queues and they dispatch tasks to different thread,
if the queue is serial, tasks executed in a line and may be in different, its not a problem.
but if we have 2 serial queues,can we manage order of them ?
if we put some Database operations in these 2 queues, the data may be wrong?

If these two queues serve different functional purposes that merit having separate queues, that's fine, but you'd probably just set up a third queue for database interaction, to which those two would dispatch their database interactions. Let this third queue, a dedicate low-level database queue, coordinate all interaction with the database.
If these two queues are just two random database interaction queues, then one might argue for refactoring the code to consolidate them into one.

Yes, you can manage the order. But not with dispatch_queue. You have to use NSOperationQueue:
NSOperationQueue * queue = [[NSOperationQueue alloc] init];
NSBlockOperation * operation1 = [NSBlockOperation blockOperationWithBlock:^{
// do something
}];
[queue addOperation:operation1];
NSBlockOperation * operation2 = [NSBlockOperation blockOperationWithBlock:^{
// do something
}];
[operation2 addDependency:operation1];
[queue addOperation:operation2];
In this code operation2 will start only when operation1 did complete.

Related

Running multiple background threads iOS

Is it possible to run multiple background threads to improve performance on iOS . Currently I am using the following code for sending lets say 50 network requests on background thread like this:
dispatch_async(dispatch_get_global_queue( DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^(void){
// send 50 network requests
});
EDIT:
After updating my code to something like this no performance gain was achieved :( Taken from here
dispatch_queue_t fetchQ = dispatch_queue_create("Multiple Async Downloader", NULL);
dispatch_group_t fetchGroup = dispatch_group_create();
// This will allow up to 8 parallel downloads.
dispatch_semaphore_t downloadSema = dispatch_semaphore_create(8);
// We start ALL our downloads in parallel throttled by the above semaphore.
for (NSURL *url in urlsArray) {
dispatch_group_async(fetchGroup, fetchQ, ^(void) {
dispatch_semaphore_wait(downloadSema, DISPATCH_TIME_FOREVER);
NSMutableURLRequest *headRequest = [NSMutableURLRequest requestWithURL:url cachePolicy: NSURLRequestUseProtocolCachePolicy timeoutInterval:60.0];
[headRequest setHTTPMethod: #"GET"];
[headRequest addValue: cookieString forHTTPHeaderField: #"Cookie"];
NSOperationQueue *queue = [[[NSOperationQueue alloc] init] autorelease];
[NSURLConnection sendAsynchronousRequest:headRequest
queue:queue // created at class init
completionHandler:^(NSURLResponse *response, NSData *data, NSError *error){
// do something with data or handle error
NSLog(#"request completed");
}];
dispatch_semaphore_signal(downloadSema);
});
}
// Now we wait until ALL our dispatch_group_async are finished.
dispatch_group_wait(fetchGroup, DISPATCH_TIME_FOREVER);
// Update your UI
dispatch_sync(dispatch_get_main_queue(), ^{
//[self updateUIFunction];
});
// Release resources
dispatch_release(fetchGroup);
dispatch_release(downloadSema);
dispatch_release(fetchQ);
Be careful not to confuse threads with queues
A single concurrent queue can operate across multiple threads, and GCD never guarantees which thread your tasks will run on.
The code you currently have will submit 50 network tasks to be run on a background concurrent queue, this much is true.
However, all 50 of these tasks will be executed on the same thread.
GCD basically acts like a giant thread pool, so your block (containing your 50 tasks) will be submitted to the next available thread in the pool. Therefore, if the tasks are synchronous, they will be executed serially. This means that each task will have to wait for the previous one to finish before preceding. If they are asynchronous tasks, then they will all be dispatched immediately (which begs the question of why you need to use GCD in the first place).
If you want multiple synchronous tasks to run at the same time, then you need a separate dispatch_async for each of your tasks. This way you have a block per task, and therefore they will be dispatched to multiple threads from the thread pool and therefore can run concurrently.
Although you should be careful that you don't submit too many network tasks to operate at the same time (you don't say specifically what they're doing) as it could potentially overload a server, as gnasher says.
You can easily limit the number of concurrent tasks (whether they're synchronous or asynchronous) operating at the same time using a GCD semaphore. For example, this code will limit the number of concurrent operations to 6:
long numberOfConcurrentTasks = 6;
dispatch_semaphore_t semaphore = dispatch_semaphore_create(numberOfConcurrentTasks);
for (int i = 0; i < 50; i++) {
dispatch_async(concurrentQueue, ^{
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
[self doNetworkTaskWithCompletion:^{
dispatch_semaphore_signal(semaphore);
NSLog(#"network task %i done", i);
}];
});
}
Edit
The problem with your code is the line:
dispatch_queue_t fetchQ = dispatch_queue_create("Multiple Async Downloader", NULL);
When NULL is passed to the attr parameter, GCD creates a serial queue (it's also a lot more readable if you actually specify the queue type here). You want a concurrent queue. Therefore you want:
dispatch_queue_t fetchQ = dispatch_queue_create("Multiple Async Downloader", DISPATCH_QUEUE_CONCURRENT);
You need to be signalling your semaphore from within the completion handler of the request instead of at the end of the request. As it's asynchronous, the semaphore will get signalled as soon as the request is sent off, therefore queueing another network task. You want to wait for the network task to return before signalling.
[NSURLConnection sendAsynchronousRequest:headRequest
queue:queue // created at class init
completionHandler:^(NSURLResponse *response, NSData *data, NSError *error){
// do something with data or handle error
NSLog(#"request completed");
dispatch_semaphore_signal(downloadSema);
}];
Edit 2
I just noticed you are updating your UI using a dispatch_sync. I see no reason for it to be synchronous, as it'll just block the background thread until the main thread has updated the UI. I would use a dispatch_async to do this.
Edit 3
As CouchDeveloper points out, it is possible that the number of concurrent network requests might be being capped by the system.
The easiest solution appears to be migrating over to NSURLSession and configuring the maxConcurrentOperationCount property of the NSOperationQueue used. That way you can ditch the semaphores altogether and just dispatch all your network requests on a background queue, using a callback to update the UI on the main thread.
I am not at all familiar with NSURLSession though, I was only answering this from a GCD stand-point.
You can send multiple requests, but sending 50 requests in parallel is usually not a good idea. There is a good chance that a server confronted with 50 simultaneous request will handle the first few and return errors for the rest. It depends on the server, but using a semaphore you can easily limit the number of running requests to anything you like, say four or eight. You need to experiment with the server in question to find out what works reliably on that server and gives you the highest performance.
And there seems to be a bit of confusion around: Usually all your network requests will run asynchronously. That is you send the request to the OS (which goes very quick usually), then nothing happens for a while, then a callback method of yours is called, processing the data. Whether you send the requests from the main thread or from a background thread doesn't make much difference.
Processing the results of these requests can be time consuming. You can process the results on a background thread. You can process the results of all requests on the same serial queue, which makes it a lot easier to avoid multithreading problems. That's what I do because it's easy and even in the worst case uses one processor for intensive processing of the results, while the other processor can do UI etc.
If you use synchronous network requests (which is a bad idea), then you need to dispatch each one by itself on a background thread. If you run a loop running 50 synchronous network requests on a background thread, then the second request will wait until the first one is completely finished.

Does AFHTTPRequestOperationManager run requests on the operationQueue?

If I have a manager created and instance-varred and I do:
AFHTTPRequestOperation *operation = [self.manager HTTPRequestOperationWithRequest:request success:mySuccessBlock failure:myFailureBlock];
[operation start];
will this run on the manager's operationQueue? I can't seem to find anything guaranteeing it will verse using one of the GET, POST, PUT methods instead which I assume will add the operation to the queue.
I see mattt's answer here, but I want to be sure one way or the other.
How send request with AFNetworking 2 in strict sequential order?
What I'm trying to do is queue up requests and have them run synchronously by setting
[self.manager.operationQueue setMaxConcurrentOperationCount:1];
If you want to run an operation on a queue, you have to explicitly add the operation to the queue:
AFHTTPRequestOperation *operation = [self.manager HTTPRequestOperationWithRequest:request success:mySuccessBlock failure:myFailureBlock];
[queue addOperation:operation];
This presumes that you would create a queue:
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
queue.name = #"com.domain.app.networkQueue";
queue.maxConcurrentOperationCount = 1;
If you just start the operations without adding them to a queue (e.g. [operation start]), they'll just run, without honoring any particular queue parameters.
Note, you could also have added your operation to the operation manager's queue (which you can set the maxConcurrentOperationCount, as you alluded to in your question):
[self.manager.operationQueue addOperation:operation];
That saves you from having to create your own queue. But it then presumes that all of your other AFNetworking requests for this manager would also run serially, too. Personally, I'd leave manager.operationQueue alone, and create a dedicated queue for my logic that required serial operations.
One final note: Using serial network operations imposes a significant performance penalty over concurrent requests. I'll assume from your question that you absolutely need to do it serially, but in general I now design my apps to use concurrent requests wherever possible, as it's a much better UX. If I need a particular operation to be serial (e.g. the login), then I do that with operation dependencies or completion blocks, but the rest of the app is running concurrent network requests wherever possible.

NSOperation and NSOperationQueue working thread vs main thread

I have to carry out a series of download and database write operations in my app. I am using the NSOperation and NSOperationQueue for the same.
This is application scenario:
Fetch all postcodes from a place.
For each postcode fetch all houses.
For each house fetch inhabitant details
As said, I have defined an NSOperation for each task. In first case (Task1), I am sending a request to server to fetch all postcodes. The delegate within the NSOperation will receive the data. This data is then written to database. The database operation is defined in a different class. From NSOperation class I am making a call to the write function defined in database class.
My question is whether the database write operation occur in main thread or in a background thread? As I was calling it within a NSOperation I was expecting it to run in a different thread (Not MainThread) as the NSOperation. Can someone please explain this scenario while dealing with NSOperation and NSOperationQueue.
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.
If you want to perform the database writing operation in the background thread you need to create a NSManagedObjectContext for that thread.
You can create the background NSManagedObjectContext in the start method of your relevant NSOperation subclass.
Check the Apple docs for Concurrency with Core Data.
You can also create an NSManagedObjectContext that executes requests in its own background thread by creating it with NSPrivateQueueConcurrencyType and performing the requests inside its performBlock: method.
From NSOperationQueue
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.
So,
[NSOperationQueue mainQueue] // added operations execute on main thread
[NSOperationQueue new] // post-iOS4, guaranteed to be not the main thread
In your case, you might want to create your own "database thread" by subclassing NSThread and send messages to it with performSelector:onThread:.
The execution thread of NSOperation depends on the NSOperationQueue where you added the operation. Look out this statement in your code -
[[NSOperationQueue mainQueue] addOperation:yourOperation]; // or any other similar add method of NSOperationQueue class
All this assumes you have not done any further threading in main method of NSOperation which is the actual monster where the work instructions you have (expected to be) written.
However, in case of concurrent operations, the scenario is different. The queue may spawn a thread for each concurrent operation. Although it's not guarrantteed and it depends on system resources vs operation resource demands at that point in the system. You can control concurrency of operation queue by it's maxConcurrentOperationCount property.
EDIT -
I found your question interesting and did some analysis/logging myself. I have NSOperationQueue created on main thread like this -
self.queueSendMessageOperation = [[[NSOperationQueue alloc] init] autorelease];
NSLog(#"Operation queue creation. current thread = %# \n main thread = %#", [NSThread currentThread], [NSThread mainThread]);
self.queueSendMessageOperation.maxConcurrentOperationCount = 1; // restrict concurrency
And then, I went on to create an NSOperation and added it using addOperation. In the main method of this operation when i checked for current thread,
NSLog(#"Operation obj = %#\n current thread = %# \n main thread = %#", self, [NSThread currentThread], [NSThread mainThread]);
it was not as main thread. And, found that current thread object is not main thread object.
So, custom creation of queue on main thread (with no concurrency among its operation) doesn't necessarily mean the operations will execute serially on main thread itself.
The summary from the docs is operations are always executed on a separate thread (post iOS 4 implies GCD underlying operation queues).
It's trivial to check that it is indeed running on a non-main thread:
NSLog(#"main thread? %#", [NSThread isMainThread] ? #"YES" : #"NO");
When running in a thread it's trivial to use GCD/libdispatch to run something on the main thread, whether core data, user interface or other code required to run on the main thread:
dispatch_async(dispatch_get_main_queue(), ^{
// this is now running on the main thread
});
If you're doing any non-trivial threading, you should use FMDatabaseQueue.

Grand Central Dispatch and concurrent tasks

I need to perform three tasks that are independent one from each other, so I'd like to execute them concurrently. But I need them all to have finished to notify another object. AFAIK, *dispatch_apply* creates concurrent threads, but it iterates a collection or an array of objects and performs the same task a number of loops, and I want to perform a different task for each thread. Is it possible to do what I want by using GCD? If not, what should be the best way?
Thanks!
Use a dispatch_group. The Concurrency Programming Guide gives one example, and there is more API that might also help you.
Create a dispatch group using dispatch_group_create.
Put each "task" in the group by enqueuing it using dispatch_group_async.
(Or, manually tell GCD when each task starts and stops, using dispatch_group_enter and dispatch_group_leave.)
To run a block when all tasks in the group have completed, enqueue it using dispatch_group_notify.
(Or, in the unlikely case that the design of your app allows you to wait synchronously, use dispatch_group_wait instead.)
When you're done with the group, dispatch_release it.
Dispatch Groups are what you need. Look at the GCD functions. What you will do is create the group and a concurrent queue (or use thee standard ones). Associate the three operations (blocks) with the dispatch_group, then create a forth block, and have that block do a dispatch_group_wait (typing names by memory), then when that returns, it can post a 'success' message to some other object. Put the block with the wait into any concurrent queue.
I do exactly this in my app.
I know you asked about GCD, but NSOperationQueue is another possibility. For example:
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
queue.maxConcurrentOperationCount = 3;
// create my completion operation (which will be added to the queue later, once
// the dependencies with all of the other operations has been established)
NSBlockOperation *completionOperation = [NSBlockOperation blockOperationWithBlock:^{
NSLog(#"All done");
}];
// let's add our three operations
NSBlockOperation *operation;
operation = [NSBlockOperation blockOperationWithBlock:^{
NSLog(#"starting task 1");
sleep(5);
NSLog(#"stopping task 1");
}];
[completionOperation addDependency:operation];
[queue addOperation:operation];
operation = [NSBlockOperation blockOperationWithBlock:^{
NSLog(#"starting task 2");
sleep(4);
NSLog(#"stopping task 2");
}];
[completionOperation addDependency:operation];
[queue addOperation:operation];
operation = [NSBlockOperation blockOperationWithBlock:^{
NSLog(#"starting task 3");
sleep(6);
NSLog(#"stopping task 3");
}];
[completionOperation addDependency:operation];
[queue addOperation:operation];
// now let's add the completion operation (which has been configured as dependent
// upon the other operations
[queue addOperation:completionOperation];
There are tons of different ways of tackling this problem, but NSOperationQueue is another option. The Concurrency Programming Guide discusses all of the options.

iOS develop about NSOperationQueue

I know the two ways to get a operation queue as follow:
queue1 = [[NSOperationQueue alloc] init];
queue2 = [[NSOperationQueue mainQueue] retain];
But I don't know what differences between them.
[queue1 addOperation:operation1];
[queue2 addOperation:operation2];
which thread does operation1 run on?main thread? or uncertainty?
I tested.
operation1 --> sometimes mainthread sometimes not.
operation2 --> always mainthread.
According to the documentation NSOperationQueue's:
create separate threads for non-concurrent operations and launch
concurrent operations from the current thread.
This explains why some of your task operate on the main thread and others don't.
The mainQueue is bound to the main thread, so operations are always performed on the main thread.
Yep, Stephen is correct.
The main purpose is to Create separate threads for non-concurrent operations and launch concurrent operations from the current thread.
In this case
queue1 = [[NSOperationQueue alloc] init];
the queue1 is the queue which belongs to the thread from which you have invoked i.e., if the above line is invoked from a detached thread then it would not belong to main thread.
but with
queue2 = [[NSOperationQueue mainQueue] retain];
You are externally getting the queue from the ios in other words 1st is local to the VC/Class called and second one is global (one for one application in ios 4)

Resources