I have a task which is reading from a disk, potentially going to take quite some time, so don't want to do it in a main thread.. and what I want is to call a function X after reading from the disk. What is the best way to do this in iOS?
So far this is what I've tried:
NSInvocationOperation *processDataOperation = [[NSInvocationOperation alloc] initWithTarget:self selector:#selector(readDisk:) object:nil];
[processDataOperation setQueuePriority:NSOperationQueuePriorityVeryHigh];
[processDataOperation setCompletionBlock:^(void){
NSMutableArray *feedItemsArray = [self generateFeedItemsFromDictionary:streamDiskData];
[self postFetchCompletedNotificationForDict:queryStringDict withFeedItems:feedItemsArray isFresh:NO];
}];
basically I am using NSInvocationOperation and then set it's completion block, however the issue is that in my completion block I need the result that is generated in readDisk. How do I access that in the completion block? It's nearly imposible right?
Using NSInvocations it is possible, but far more complicated than necessary, to achieve a trivial amount of work beyond the main thread.
Both GCD and NSOperations can be used to implement a wide array of concurrency strategies. From an object-oriented perspective, NSOperations are more highly abstracted than CGD blocks, which makes them (imo) easier to "design" with, and potentially optimized beyond the scope of where I'm implementing them. GCD is lower-level: This makes interacting with it appear slightly more complicated (it really isn't), but people who are in to that sorta stuff will tell you that it is "more efficient" and carries "less overhead".
My personal approach is to use NSOperations in scenarios where I have a designed/orchestrated concurrency pattern in my application, and use GCD for trivial concurrent/background operations.
If all I need to do is fire some arbitrary task that is not relevant to the design but needs to be done in the background, I'd use CGD. That's what I'd probably use in this case:
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^{
[self readDisk];
NSMutableArray *feedItemsArray = [weakSelf generateFeedItemsFromDictionary:streamDiskData];
dispatch_sync(dispatch_get_main_queue(), ^{
//Call back to the main thread before performing/posting anything touching UIKit
[self postFetchCompletedNotificationForDict:queryStringDict withFeedItems:feedItemsArray isFresh:NO];
})
})];
You could always use grand central dispatch to do your operation in the background instead.
Since it is a block you can just call the method normally and store the result. Then grab the main queue if you need to update any UI or do whatever you need to after completion.
dispatch_queue_t queue = dispatch_queue_create("read disc", NULL);
dispatch_async(queue, ^{
result = [self readDisc];
dispatch_async(dispatch_get_main_queue(), ^{
//update UI or do whatever you need to do with the result of readDisc
});
});
dispatch_release(queue);
Related
Guys I need some help to architect my multithreading in iOS.
I'm using ARC in my code.
So basically I need following,
In my main thread nstimer fire some method which should be executed in a separate thread, that thread does some calculation and puts data into some ivar, and another thread should read data from that ivar and do some other calculation, i.e. if there is no data the second thread should wait until there is any.
So basically I would like to hear some advice which technology is the best choice for my task, to use cocoa thread (NSThread), GCD or Operation queues.
Also can someone please provide me with some pseudo code on aspects of mutual blocking/synchronization between two threads.
Since you are saying that some calculations should wait for other calculations to finish, I would say that you should have a look at NSOperation and set dependencies for the different operations (using addDependency).
Unless you left something our of your problem description, that is a perfect fit for GCD/block combo. In fact, I wouldn't even use a NSTimer (GCD provides a better alternative - see dispatch_source_create for example of creating GCD based timer), but that's your call, and not what the question asked. Anyway, with GCD...
- (void)handleTimer:(NSTimer *)timer {
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^{
__block id someObject;
// Do work... manipulate someObject in some manner...
// When done, invoke other thread... main thread in this case
dispatch_async(dispatch_get_main_queue(), ^{
// This code is running in a different thread, and can use someObject directly
});
});
}
I've implemented a block that is dispatched asynchronously using GCD as follows:
__block BOOL retValue;
dispatch_async(dispatch_get_global_queue(0, 0), ^{
retValue = [self GCDHandler:actionName WithServiceType:serviceType :arguments];
});
return retValue;
How do I cancel such a block if it is running for longer than I would like? Is there a way to cancel GCD-dispatched blocks, or provide a timeout to them?
There is no built in way to cancel GCD blocks. They're rather set and forget. One way I've done this in the past is to provide 'tokens' for blocks.
- (NSString*)dispatchCancelable:(dispatch_block_t)block
{
dispatch_async(dispatch_get_global_queue(0, 0), ^{
if (!checkIfCanceled)
block();
}
return blah; //Create a UUID or something
}
- (void)cancelBlock:(NSString*)token
{
//Flag something to mark as canceled
}
That depends on what your GCDHandler is doing. There's some pretty good videos about GCD on the Apple dev site - you might want to move up a layer (into Cocoa) and use NSOperationQueue and NSOperations (either your own subclass or NSBlockOperation). They're all built on top of GCD and the abstraction layer might be more appropriate for what you are trying to do (which you don't state - is it a network request? etc.)
I have a low-priority PDF-drawing NSOperation. I am using the function CGContextDrawPDFPage to draw the page.
As best I can tell, the app will not switch to another thread while the CGContextDrawPDFPage function is executing. I suppose I need to break up my graphics context into several smaller contexts to get around that. But I still have one problem -- how can I persuade the operation to allow other threads to perform their tasks before starting that draw? The way things are set up right now, the app draws the page right away, even though the thread priority is set to zero.
Here is the code that adds the NSOperation to the queue:
MyNSOperationSubclass* op = [[MyNSOperationSubclass alloc]initWithStuff: stuff];
[op setThreadPriority:0.];
[self.drawingQueue addOperation:op];
Here is some code within MyNSOperationSubclass:
-(void) drawStuff {
NSLog(#"drawStuff");
#autoreleasepool {
// some code to setup the graphics context and PDF page here
CGContextDrawPDFPage(context, page);
[self finish]; // finishes the NSOperation
}
}
-(void) main {
NSLog(#"main");
#autoreleasepool {
[self drawStuff];
}
}
-(void) start {
self.isExecuting = YES;
[NSThread detachNewThreadSelector:#selector(main) toTarget:self withObject:nil];
}
what happens is that even though the thread priority is set to zero, the setup code is so quick that the NSOperation gets into the CGContextDrawPDFPage call right as the operation is started. But once that call has started, the thread won't yield to other threads until it has finished. So the drawing operation happens right away, which is the opposite of what I want to do.
EDIT: After taking a closer look, the first answerer is correct that the CGContextDrawPDFPage call does not always prevent a thread from yielding. But even with a non-concurrent operation, I still have the problem that it finishes quickly, rather than waiting around for other, higher-priority stuff to happen.
EDIT 2: Apparently I don't understand how thread priorities work. After changing it to a nonconcurrent operation, the thread priority of the operation thread is 0.5, even though I called [op setThreadPriority: 0] before adding it to the queue.
So, the first thought is that your thread can't avoid "yielding" to other threads. Context switching is pre-emptive here. So you might want to talk about why you think this thread is not yielding. You may have some other bigger issue here with the other work that you are expecting to be done.
Second, when you set the thread priority for this operation, you're not setting the priority of this new thread that you are creating yourself here:
[NSThread detachNewThreadSelector:#selector(main) toTarget:self withObject:nil];
That's a new thread with normal priority.
But stepping back from that a second, why not just make this a non-concurrent operation and let your NSOperationQueue manage the thread for you? The queue will create a new thread for you as needed (and apply your thread priority that you asked for).
Alternatively, unless you still need to support iOS3, you can just use the global low priority GCD queue for this work (or target your own GCD queue to that global queue if you need finer control over order and still want all of this done with low priority).
So, consider letting either NSOperationQueue or GCD manage some of this for you. And think about why you think this thread is interfering with other work. That's probably some bigger issue.
Hope that helps.
In the scenario in which i have a thread launched, can i still acces methods on the parent thread? Is there a specific way to call this methods? If so, what is it?
Note: in my scenario both thread are for data manipulation, they are not interface-related threads ( i know this was to be considered in .NET, don't know it they are in Objective-c).
In this case, it is best to use Grand Central Dispatch (GCD) instead of working with NSThead or NSOperation directly.
Overview of Concurrency: http://developer.apple.com/library/ios/#documentation/General/Conceptual/ConcurrencyProgrammingGuide/Introduction/Introduction.html#//apple_ref/doc/uid/TP40008091
Intro to Grand Central Dispatch: http://cocoasamurai.blogspot.com/2009/09/guide-to-blocks-grand-central-dispatch.html
With your example, you can use nested calls into Grand Central Dispatch to achieve this functionality:
dispatch_queue_t backgroundQueue = dispatch_queue_create("com.example.exampleQueue", 0);
dispatch_async(backgroundQueue, ^{
// operate on data in the background here
NSData *stuff = [self doSomethingComplex];
dispatch_async(dispatch_get_main_queue(), ^{
// Perform Task back in the main thread
[viewController updateStuff:stuff];
});
});
This method is the preferred method for performing these kind of tasks. In addition, by utilizing blocks, it is also very easy to understand the code at a glance without having to example multiple methods within your class.
Threads by definition share the state of parent thread. In ObjectiveC, if you spawn a worker thread & want to call some method on main thread, this can be done like so-
[self performSelectorOnMainThread:#selector(someMethod:) withObject:nil waitUntilDone:NO];
If they are not interface stuff, or can result in some interface stuff you can call then you can just call then, and do any of the usual thread safety stuff you have to do in any language, like #syschronise(obj) or NSLock. But if it is stuff that will result in interface stuff then you will have to do as 'Srikar' wrote [self performSelectorOnMainThread:#selector(setDataCount:) withObject:count waitUntilDone:NO]; which will effectively place the message onto the NSRunLoop cue.
I have a toolkit that I need to work with (to interface with a remote service). This toolkit queries the remote service and asks for results. It does this asynchronously, which in most cases is good, but not for creating concise methods. I want to make methods similar to the following:
-(NSArray *)getAllAccounts {
NSString *query = #"SELECT name FROM Account";
//Sets "result" to the query response if no errors.
//queryResult:error:context: is called when the data is received
[myToolkit query:query target:self selector:#selector(queryResult:error:context:) context:nil];
//Wait?
return result.records;
}
The problem is, inside the toolkit the methods call each other using #selector, not direct calls, so getting return values is difficult. Further, the actual query uses:
NSURLConnection *connection = [[[NSURLConnection alloc] initWithRequest:aRequest delegate:self] autorelease];
Which is asynchronous. By the time the data has been received from the service, my method has long ago returned... without the information. So my question is this: Is there a way to pause execution until the data has been returned? Could I accomplish this using a second thread to get the data while the main thread rests (or using 3 threads so the main thread doesn't rest?)
I don't want to edit the toolkit to change their method (or add a new one) to be synchronous, so is there a way to make a method as I want?
You might want to consider NOT making it all synchronous, especially if the sample code in your post is run on your main application thread. If you do that, the main thread will block the UI and the application will cease to respond until the remote transaction is complete.
Therefore, if you really insist on the synchronous approach, then you should definitely do it in a background thread so that the UI does not become unresponsive, which can actually lead to your App getting killed by the OS on iphone.
To do the work in a background thread, I would strongly recommend using the Grand Central Dispatch stuff, namely NSBlockOperation. It will free you from having to actually create and manage threads and makes your code pretty neat.
To do the synchronous thing, take a look at the NSCondition class documentation. You could do something like the following:
NSCondition* condition = ...;
bool finished = NO;
-(NSArray *)getAllAccounts {
[condition lock];
NSString *query = #"SELECT name FROM Account";
//Sets "result" to the query response if no errors.
//queryResult:error:context: is called when the data is received
[myToolkit query:query target:self selector:#selector(queryResult:error:context:) context:nil];
while (!finished)
[condition wait];
[condition unlock];
return result.records;
}
Then in the method called by the toolkit to provide the results you'd do:
- (void) queryResult:error:context: {
// Deal with results
[condition lock]
finished = YES;
[condition signal];
[condition unlock];
}
You'd probably want to encapsulate the "condition" and "finished" variables in your class declaration.
Hope this helps.
UPDATE: Here is some code to offload the work to a background thread:
NSOperationQueue* queue = [NSOperationQueue new];
[queue addOperationWithBlock:^{
// Invoke getAllAccounts method
}];
Of course, you can keep the queue around for later use and move the actual queuing of the work to inside your method call to make things neater.
The way to wait is to return from your current code. Finish up doing what you want done after the wait, in the asynchronous callback method you specify. What's so difficult about that?
Any synchronous waits in the main UI thread will block the UI and make the user think your app has locked up, which is likely far worse than your thinking the code isn't concise enough.