NSOperationQueue not reusing thread on iPhone - iphone

I'm using iPhone SDK 3.1.2, and the following code shows the NSOperationQueue does not reuse the thread for each task.
The code does not have any problems on Snow Leopard.
- (void)applicationDidFinishLaunching:(UIApplication *)application {
// Override point for customization after app launch
[window addSubview:viewController.view];
[window makeKeyAndVisible];
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
[queue setMaxConcurrentOperationCount:1];
for(int i = 0; i < 100; i++) {
NSInvocationOperation *op = [[NSInvocationOperation alloc] initWithTarget:self selector:#selector(run) object:nil];
[queue addOperation:op];
[op release];
}
}
- (void)run {
static int tc = 0;
if([[NSThread currentThread] isMainThread]) {
NSLog(#"MAIN THREAD");
return;
} else if([[NSThread currentThread] name] == nil) {
[[NSThread currentThread] setName:[NSString stringWithFormat:#"THREAD_%d", tc++]];
}
NSLog(#"%#", [[NSThread currentThread] name]);
}
The output shows it create 100 threads to execute the 100 tasks.
2010-01-07 11:46:03.502 OperationQueueTest[7911:4503] THREAD_0
2010-01-07 11:46:03.506 OperationQueueTest[7911:4d03] THREAD_1
2010-01-07 11:46:03.507 OperationQueueTest[7911:4807] THREAD_2
2010-01-07 11:46:03.510 OperationQueueTest[7911:4d07] THREAD_3
2010-01-07 11:46:03.514 OperationQueueTest[7911:5007] THREAD_4
2010-01-07 11:46:03.516 OperationQueueTest[7911:4f0b] THREAD_5
2010-01-07 11:46:03.518 OperationQueueTest[7911:4e0f] THREAD_6
...
2010-01-07 11:46:03.740 OperationQueueTest[7911:4ea7] THREAD_97
2010-01-07 11:46:03.744 OperationQueueTest[7911:4dcf] THREAD_98
2010-01-07 11:46:03.746 OperationQueueTest[7911:460f] THREAD_99

NSOperationQueue is designed to pool and re-use threads in the most efficient way possible and in this instance, it seems it decided not re-using threads was the best way to go.
Test code has it's uses (and it is possible you may have identified a corner case where NSOperationQueue does not do the most efficient thing), but that doesn't mean that NSOperationQueue is always horribly inefficient when dealing with real code in real life; in fact my own experience has been to the contrary.
So I'd say use it in your real code and if you have performance issues, dig further into what it's doing with threads behind the scenes. Otherwise don't worry about it.
As an aside, if you are still curious, you might try recording the names of the threads into an array of NSStrings and then printing everything out at the end of the test code, rather than logging as you go along - this will significantly reduce the amount of work done by each NSInvocationOperation.

Snow Leopard's implementation of NSOperation/NSOperationQueue is now based on GCD.
The iPhone still uses the old Leopard implementation. So you can expect different results on each platform (Not to mention the completely different hardware).
It is possible that spawning new threads is the most efficient way to accomplish the tasks you are giving NSOperationQueue.

NSOperationQueue performs it's added operations asynchronously (in separate thread). So if we print thread information, then it is possible that we will get same thread object most of the time.
NSOperation object added in NSOperationQueue will different but thread objects can be same.
- (void)operaitonqueueTest
{
_opQueue = [[NSOperationQueue alloc] init];
[_opQueue setMaxConcurrentOperationCount:5];
for(int i = 0; i < 50; i++) {
NSInvocationOperation *op = [[NSInvocationOperation alloc] initWithTarget:self selector:#selector(run) object:nil];
[_opQueue addOperation:op];
}
}
- (void)run {
if([[NSThread currentThread] isMainThread]) {
NSLog(#"MAIN THREAD");
return;
}
NSLog(#"currentThread = %#", [NSThread currentThread]);
}

Related

How perform Some task In Background in iOS

I want perform some database related task in background for that I have added code
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,
0), ^(void) {
[lclDB deleteRecoredwithBlock:^(BOOL success) {
if (success) {
NSLog(#"Deletion Succesful...");
}
}];
});
deleteRecord function internally calls number of methods sequentially to perform delete operation in local database.now I have wait until all delete operation is performed.but I want to do this whole delete operation in background.if any one known please help me to figure out these problem.
Any NSObject can perform action in background using the following :
[myObject performSelectorInBackground:#selector(anAction) withObject:nil];
More information on apple documentation.
Try performSelectorInBackground:withObject: method.
[self performSelectorInBackground:#selector(backgroundMethod) withObject:nil];
You can also use NSInvocationOperation.
NSOperationQueue *queue = [NSOperationQueue new];
NSInvocationOperation *operation = [[NSInvocationOperation alloc] initWithTarget:self selector:#selector(deleteDataWithOperation) object:nil];
[queue addOperation:operation];
And this is your deleteDataWithOperation method -
-(void)deleteDataWithOperation
{
//Do your work here
}

Using an application-lifetime-thread other than the main thread

I've a multi-threading application in which each thread has to do some job, but at a certain point some code needs to be executed serially (like writing into sqlite3 database), so I'm calling that code to be performed on main thread using:
[self performSelectorOnMainThread:#selector(serialJob:) withObject:object waitUntilDone:YES];
and every thing went just fine except that when that code needs some time the user interaction with the application gets disabled until that code has been finished, so is there any way to make another ONE thread that can be run on background and can be called whenever I need it just like the main one so I can replace the previous call with:
[self performSelector:#selector(serialJob:) onThread:REQUIRED_THREAD withObject:object waitUntilDone:YES];
this thread should be some class's static data member to be accessed from all over the code.
any help would be very appreciated, and many thanks in advance...
This is quite easy to do, just spawn your thread and let it run it's runloop using [[NSRunLoop currentRunLoop] run]. That's all that is required to be able to use performSelector:onThread: with a custom thread.
If you are on iOS 4 or newer you should consider using Grand Central Dispatch queues instead of threads though. The GCD APIs are much easier to use and can utilize the system resources much better.
Like Sven mentioned, look into Grand Central Dispatch.
You can create a queue like this:
dispatch_queue_t myQueue = dispatch_queue_create("com.yourcompany.myDataQueue", NULL);
Now you can call blocks on that queue:
dispatch_async(myQueue, ^{
// Your code to write to DB.
});
When you're done, don't forget to release the queue:
dispatch_release(myQueue);
Due to the my question that I need the current thread to be blocked until the database job has been finished, I've tried these two solutions and they worked perfectly. You can either use critical sections or NSOperationQueue and I prefer the first one, here is the code for both of them:
define some class "DatabaseController" and add this code to its implementation:
static NSString * DatabaseLock = nil;
+ (void)initialize {
[super initialize];
DatabaseLock = [[NSString alloc] initWithString:#"Database-Lock"];
}
+ (NSString *)databaseLock {
return DatabaseLock;
}
- (void)writeToDatabase1 {
#synchronized ([DatabaseController databaseLock]) {
// Code that writes to an sqlite3 database goes here...
}
}
- (void)writeToDatabase2 {
#synchronized ([DatabaseController databaseLock]) {
// Code that writes to an sqlite3 database goes here...
}
}
OR to use the NSOperationQueue you can use:
static NSOperationQueue * DatabaseQueue = nil;
+ (void)initialize {
[super initialize];
DatabaseQueue = [[NSOperationQueue alloc] init];
[DatabaseQueue setMaxConcurrentOperationCount:1];
}
+ (NSOperationQueue *)databaseQueue {
return DatabaseQueue;
}
- (void)writeToDatabase {
NSInvocationOperation * operation = [[NSInvocationOperation alloc] initWithTarget:self selector:#selector(FUNCTION_THAT_WRITES_TO_DATABASE) object:nil];
[operation setQueuePriority:NSOperationQueuePriorityHigh];
[[DatabaseController databaseQueue] addOperations:[NSArray arrayWithObject:operation] waitUntilFinished:YES];
[operation release];
}
these two solutions block the current thread until the writing to database is finished which you may consider in most of the cases.

UIView animation blocked by busy main thread

I'm building my own activity indicator like class that's supposed to fade in before a heavy operation, and fade out when the operation is complete. This is working fine, until I run into the following scenario:
[[MyLoaderClass sharedInstance] displayLoaderInView:self.view];
for( int i = 0; i < 1000; i++ ) {
NSLog(#"Performing heavy operation...");
}
[[MyLoaderClass sharedInstance] removeLoaderInView:self.view];
What's happening on the first line is that my loader view is alloced, subviewed and told to fade in with a standard UIView animation. However, the animation doesn't start (as shown by the setAnimationWillStartSelector:) until after the heavy operation is complete.
Now, heavy operations on the main thread are of course to be avoided, but I still want my loader class to work no matter what programmers might throw at it.
I tried moving the loader into a separate thread and animating it from there which worked great, but led to crashes because it's not cool to manipulate views from threads other than the main thread.
My question: Is it possible to do what I want, and/or should I bother with it at all?
As an alternative to Joshua Smith's suggestion, in case being on a different thread messes with your operation, just make sure you drop out to the runloop between starting the UIView animations and starting your heavy code. E.g.
...
[[MyLoaderClass sharedInstance] displayLoaderInView:self.view];
[self performSelector:#selector(performHeavyOperation) withObject:nil afterDelay:0];
}
- (void)performHeavyOperation
{
for( int i = 0; i < 1000; i++ ) {
NSLog(#"Performing heavy operation...");
}
[[MyLoaderClass sharedInstance] removeLoaderInView:self.view];
}
The performSelector:withObject:afterDelay: causes the nomated selector to be scheduled on the runloop in the future. Setting a delay of 0 means it is added to the runloop to occur as soon as possible.
For various reasons, quite a lot of UIView stuff takes effect only if you allow the call stack to unwind all the way to the call stack. That's so that, e.g. if you did:
view.frame = aNewFrame;
view.someOtherPropertyThatWouldCauseAVisibleChange = anotherValue;
Then the UIView will end up redrawing itself only once, not twice.
Put your heavy operation in an NSOperationQueue, then it will not block the main thread.
#interface MyClass : NSOperation {
}
#end
#implementation MyClass
-(void) main {
for( int i = 0; i < 1000; i++ ) {
NSLog(#"Performing heavy operation...");
}
}
#end
Then, in your above code:
[[MyLoaderClass sharedInstance] displayLoaderInView:self.view];
NSOperationQueue *q = [[NSOperationQueue alloc] init];
MyClass *c = [[[MyClass alloc] init] autorelease];
[q addOperation:c];
[q waitUntilAllOperationsAreFinished];
[[MyLoaderClass sharedInstance] removeLoaderInView:self.view];
Read the docs, too, you'll need them: http://developer.apple.com/library/mac/#documentation/cocoa/reference/NSOperationQueue_class/Reference/Reference.html
NSOperationQueue's are awesome, but not exactly intuitive.

Keeping track of multiple threads on iphone

I'm trying to do something basic to understand threads with a counter that just increments when a button is pressed, and every time the button is pressed, a new thread incrementing the same counter starts. Then I have a stop button to stop a thread that is running. How can I tell how many threads, or which thread is running? Here is my basic template I am working on. Thanks.
-(int)count {
return count;
}
-(void)setCount:(int) value {
count = value;
}
-(void)updateDisplay {
countLabel = [NSString stringWithFormat:#"%i", count];
count++;
}
-(void)myThread {
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
[self performSelectorOnMainThread:#selector(updateDisplay)
withObject:nil
waitUntilDone:NO];
[pool release];
}
-(void)startThread {
[self performSelectorInBackground:#selector(myThread) withObject:nil];
}
-(void)myThreadStop {
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
[self performSelectorOnMainThread:#selector(updateDisplay)
withObject:nil
waitUntilDone:NO];
[pool release];
}
-(void)stopThread {
[self performSelectorInBackground#selector(myThreadStop) withObject:nil];
}
Basically, you want to keep track of the number of threads you have running, and also assign each thread a unique ID. Assuming that startThread is an event handler for your button, you might have something like:
static int threadIndex = 0;
static int threadsRunning = 0;
-(void)startThread {
NSNumber* threadId = [NSNumber numberWithInt:threadIndex++];
threadsRunning++;
[self performSelectorInBackground:#selector(myThread) withObject:threadId];
}
Then when you stop a thread, you just decrement threadsRunning.
Looking at your code, though, I'm confused by your stopTread method, since it seems to be doing the exact same thing as the myThread method, i.e., not stopping the thread at all.
You're performing stuff in the background, which is distinct from explicitly creating threads (e.g. it might reuse threads in a thread pool).
If you want really inefficient threading code, you could use something like this:
NSThread * thread = [[[NSThread alloc] initWithTarget:self selector:#selector(myThread) object:nil] autorelease];
[thread start];
while ([thread isExecuting])
{
NSLog(#"Still running");
[NSThread sleepForTimeInterval:0.1];
}
EDIT: If you're actually going to do iPhone development, I recommend looking at NSOperation/NSInvocationOperation/NSBlockOperation instead. Thread management is a real pain to get right.

Is calling -setNeedsDisplay from a background task asking for trouble?

I have a background task that updates a view. That task calls -setNeedsDisplay to have the view drawn.
This works:
- (void) drawChangesTask;
{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
if (pixels) {
drawChanges((UInt32 *) origPixels, (UInt32 *) pixels, CGBitmapContextGetBytesPerRow(ctx)/4, CGBitmapContextGetHeight(ctx), count--);
if (count < 0) {
count = 150;
}
else
[self performSelectorInBackground:#selector(drawChangesTask) withObject:nil ];
[self performSelectorOnMainThread:#selector(setNeedsDisplay) withObject:nil waitUntilDone:NO ];
}
[pool release];
}
This does not work:
- (void) drawChangesTask;
{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
if (pixels) {
drawChanges((UInt32 *) origPixels, (UInt32 *) pixels, CGBitmapContextGetBytesPerRow(ctx)/4, CGBitmapContextGetHeight(ctx), count--);
if (count < 0) {
count = 150;
}
else
[self performSelectorInBackground:#selector(drawChangesTask) withObject:nil ];
[self setNeedsDisplay];
}
[pool release];
}
Anyone know why? When I say it doesn't work, I mean that it runs tens of iterations, sometimes I see portions of my image shifted up or down, or entirely blank, and then the deugger give me an “EXC_BAD_ACCESS” somewhere in CoreGraphics.
Also, if I don't handle the autorelease pool myself, then I get leaking error messages. Don't understand why that is either. My drawChanges() doesn't create any new objects. Here's the error:
2009-08-17 11:41:42.358 BlurApp[23974:1b30f] *** _NSAutoreleaseNoPool(): Object 0xd78270 of class NSThread autoreleased with no pool in place - just leaking
UIKit simply isn't thread-safe — you need to call methods that update UIKit controls on the main thread.
I think that this line:
[self performSelectorInBackground:#selector(drawChangesTask) withObject:nil];
Is causing trouble. Have you tried simply calling it again on the current thread? If you need the runloop to execute between the calls, use:
[self performSelector:#selector(drawChangesTask) withObject:nil afterDelay:0.0];
This will call the method on the current thread after the method you're in has finished and the runloop has gone round once.
Problem here is that UIKit is not thread safe, if you tell your UI to do something from a background thread nothign is guaranteed, what you want to do is use the performSelectorOnMainThread method to do updates t o your UI elements