In my application I am executing 10 asynchronous NSURLConnections within an NSOperationQueue as NSInvocationOperations. In order to prevent each operation from returning before the connection has had a chance to finish I call CFRunLoopRun() as seen here:
- (void)connectInBackground:(NSURLRequest*)URLRequest {
TTURLConnection* connection = [[TTURLConnection alloc] initWithRequest:URLRequest delegate:self];
// Prevent the thread from exiting while the asynchronous connection completes the work. Delegate methods will
// continue the run loop when the connection is finished.
CFRunLoopRun();
[connection release];
}
Once the connection finishes, the final connection delegate selector calls CFRunLoopStop(CFRunLoopGetCurrent()) to resume the execution in connectInBackground(), allowing it to return normally:
- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
TTURLConnection* ttConnection = (TTURLConnection*)connection;
...
// Resume execution where CFRunLoopRun() was called.
CFRunLoopStop(CFRunLoopGetCurrent());
}
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error {
TTURLConnection* ttConnection = (TTURLConnection*)connection;
...
// Resume execution where CFRunLoopRun() was called.
CFRunLoopStop(CFRunLoopGetCurrent());
}
This works well and it is thread safe because I bundled each connection's response and data as instance variables in the TTURLConnection subclass.
NSOperationQueue claims that leaving its maximum number of concurrent operations as NSOperationQueueDefaultMaxConcurrentOperationCount allows it to adjust the number of operations dynamically, however, in this case it always decides that 1 is enough. Since that is not what I want, I have changed the maximum number to 10 and it seriously hauls now.
The problem with this is that these threads (with the help of SpringBoard and DTMobileIS) consume all of the available CPU time and cause the main thread to become latent. In other words, once the CPU is 100% utilized, the main thread is not processing UI events as fast as it needs to in order to maintain a smooth UI. Specifically, table view scrolling becomes jittery.
Process Name % CPU
SpringBoard 45.1
MyApp 33.8
DTMobileIS 12.2
...
While the user interacts with the screen or the table is scrolling the main thread's priority becomes 1.0 (the highest possible) and its run loop mode becomes UIEventTrackingMode. Each of the operation's threads are 0.5 priority by default and the asynchronous connections run in the NSDefaultRunLoopMode. Due to my limited understanding of how threads and their run loops interact based on priorities and modes, I am stumped.
Is there a way to safely consume all available CPU time in my app's background threads while still guaranteeing that its main thread is given as much of the CPU as it needs? Perhaps by forcing the main thread to run as often as it needs to? (I thought thread priorities would have taken care of that.)
UPDATE 12/23:
I have finally started getting a handle on the CPU Sampler and found most of the reasons why the UI was becoming jittery. First of all, my software was calling a library which had mutual exclusion semaphores. These locks were blocking the main thread for short periods of time causing the scroll to skip slightly.
In addition, I found some expensive NSFileManager calls and md5 hashing functions which were taking too much time to run. Allocating big objects too frequently caused some other performance hits in the main thread.
I have begun to resolve these issues and the performance is already much better than before. I have 5 simultaneous connections and the scrolling is smooth, but I still have more work to do. I am planning to write a guide on how to use the CPU Sampler to detect and fix issues that affect the main thread's performance. Thanks for the comments so far, they were helpful!
UPDATE 1/14/2010:
After achieving acceptable performance I began to realize that the CFNetwork framework was leaking memory occasionally. Exceptions were randomly (however, rarely) being raised inside CFNetwork too! I tried everything I could to avoid those problems but nothing worked. I am quite sure that the issues are due to defects within NSURLConnection itself. I wrote test programs which did nothing except exercise NSURLConnection and they were still crashing and leaking.
Ultimately I replaced NSURLConnection with ASIHTTPRequest and the crashing stopped entirely. CFNetwork almost never leaks, however, there is still one very rare leak which occurs when resolving a DNS name. I am quite satisfied now. Hopefully this information saves you some time!
In practice you simply cannot have more than two or three background network threads and have the UI stay fully responsive.
Optimize for user responsiveness, it's the only thing a user really notices. Or (and I really hate to say this) add a "Turbo" button to your app that puts up a non-interactive modal dialog and increases concurrent operations to 10 while it is up.
It sounds as though NSOperationQueueDefaultMaxConcurrentOperationCount is set to 1 for a reason! I think you're just overloading your poor phone. You may be able to mess around with threading priorities -- I think the Mach core is available and part of the officially blessed API -- but to me it sounds like the wrong approach.
One of the advantages of using "system" constants is that Apple can tune the app for you. How are you going to tune this to run on an original iPhone? Is 10 high enough for next years quad-core iPhone?
James, although I haven't experienced your problem, what I've had success with is using the synchronous connection for downloading within an NSOperation subclass.
NSData *response = [NSURLConnection sendSynchronousRequest:request returningResponse:&urlResponse error:&requestError];
I use this approach for grabbing image assets from network locations and updating the target UIImageViews. The download occurs in NSOperationQueue and the method that updates the image-view is performed on the main thread.
Related
According to the documentation of CLLocationManagerDelegate
The methods of your delegate object are called from the thread in which you started the corresponding location services. That thread must itself have an active run loop, like the one found in your application’s main thread.
I am not clear as to whether this means that to receive location manager updates on a background thread, we must instantiate the location manager on that background thread or simply call the startUpdatingLocation() method on that thread.
In any event, this explains an issue when a CLLocationManagerDelegate does not receive any events from a CLLocationManager which was started on a background thread:
That thread must itself have an active run loop
If I understand run loop functioning correctly, all NSThreads are instantiated with a run loop, but the run loop will only be running if you assign some work to the thread. Therefore, to have a CLLocationManager send events correctly on a background thread, we need to set the thread's run loop to loop permanently so that it can process the CLLocationManager's calls as they arrive.
A reasonable solution to making sure the run loop is running is suggested in this question but the author implies that this is a processor expensive way of doing it.
Also, according to the threading documentation,
Threading has a real cost to your program (and the system) in terms of memory use and performance
I appreciate that we are all using lots of threading anyway, by using Grand Central Dispatch, but Grand Central Dispatch probably mitigates a lot of this in its internal thread management.
So my first question is, is it worthwhile setting up a background thread with a continuously running run loop, in order to have location events dealt with on a background thread, or will this involve an unreasonable extra amount of processing when compared to leaving the manager on the main thread?
Secondly, if it is worthwhile, is there a good way to do this using Grand Central Dispatch. As I understand the documentation, Grand Central Dispatch manages its own threads and we have no means of knowing which thread a given block will be executed on. I presume we could simply execute the usual run loop code to make the run loop of whichever thread our CLLocationManager instantiation is run on loop continuously, but might this not then affect other tasks independently assigned to Grand Central Dispatch?
This is a somewhat opinion-based question, but I have a pretty strong opinion on it :D
No.
Just deliver the events to the main queue, and dispatch any work to a background queue if it's non-trivial. Anything else is a lot of complexity for little benefit. CLLocationManager pre-dates GCD, so this was useful information in the days when we occasionally managed run loops by hand and dispatching from one thread to another was a pain. GCD gets rid of most of that, and is absolutely the tool you should use for this. Just let GCD handle it with dispatch_async.
You absolutely should not set up your own NSThread for this kind of thing. They're still necessary at times for interacting with C++, but generally if GCD can handle something, you should let it, and avoid NSThread as much as possible.
I have a background thread that is doing a bunch of work - loading the application. The main thread is displaying progress on a UIProgressView.
The background thread is being spawned with performSelectorInBackground (though, I'm not wed to this method if a different approach makes this problem easier to solve)
[self performSelectorInBackground:#selector(loadAppInBackground) withObject:self];
On a couple occasions a bug has caused the background thread to crash (different bugs as the app evolves) which results in the progress bar stopping, but the user getting no clear indication that anything is wrong.
I'd like to detect this situation and fail more gracefully than simply hanging until the user gives up on waiting.
Because the duration of the load process can vary greatly, simply timing out isn't an ideal option.
What's the best way for the foreground thread to detect that the background thread has failed? Since the foreground thread is busy dealing with the UI, would it require a second background thread to monitor the first? That seems ugly.
Is there some thread-to-thread communication mechanism that could be used to "ping" the background process? Better yet, a low level system mechanism of checking the status of other threads?
The debugger knows about all the threads that are running... and seems to know their status. I'm wondering if there's a call available to my app to do the same.
If the background task runs in some sort of regular cycle (eg, there's a big loop where much of the work gets done), it can set a flag every so often to indicate that it's still alive.
One way to do this is to have background thread store [NSDate timeIntervalSinceReferenceDate] somewhere, and, in your main thread, occasionally (perhaps on a timer) compare that to the current time. If the difference is greater than some reasonable limit you can guess that the background thread has died.
Another way is to have the background thread simply set a Boolean, and have the main thread interrogate that and clear it on some regular basis. If the Boolean doesn't get set again between main thread interrogations you can infer that it has died.
The first technique has the advantage that you can "tune" the "reasonable limit" to tolerate code (in either thread) that is somewhat irregular in it's timing. The second approach generally requires timings that are more predictable.
Of course with either approach you want to somehow avoid "blowing the whistle" if the background thread has just finished up and you simply haven't recognized that yet.
A common technique is to have an extra thread to check for life signs of the thread in question - a so called heartbeat thread. The heartbeat thread polls the thread by checking if it responds in a timely manner, if not, deems the thread dead and terminates it.
A simple heartbeat thread implementation would be to check a counter that is incremented regularly by the other thread, if the counter is not incremented within a certain time it is regarded as dead and then an appropriate action could be taken like restarting thread or killing app. Another more common way is if the hb thread sends messages to the thread and checks for a response with a timeout.
It seems like there is no mechanism in objective c to check the status of a background thread directly. Any of the answers provided are decent options... either timing out, or having the thread create some sort of evidence of its continued existence.
I was hoping for something a little more simple, reliable, and real-time.
I'm going to experiment with catching an exception in the thread and perhaps producing a notification like "BackgroundThreadException" that the foreground thread could listen for and react to.
In our iPhone application we have several tabs and selecting each tab triggers network connection. In the past we were just detaching new thread for each connection. And after several very quick tab switches application was becoming unresponsive.
Now we decided to use operation queue which supposed should control number of threads and should not allow the application to become unresponsive. But now the app becomes unresponsive even with fewer quick switches (although now it recovers from unresponsiveness quicker).
I ran the app on device from xcode and paused it after several quick switches to see the number of threads. And what I have found is that there are several threads with the following stack:
0 __workq_kernreturn
2 _init_cpu_capabilities
Any idea what are these threads and how to get rid of them?
One of the big benefits of using NSOperationQueue is that you can forget about threads and let the system worry about that for you. It sounds like the root of your problem is that you've got several operations running simultaneously that are no longer needed. Rather than worrying about the specific threads, consider getting those operations to terminate so that they're no longer using up computing resources.
For what it's worth, my guess is that those threads are being managed by Grand Central Dispatch. GCD will create worker threads to process blocks (and operations), and it'll be as efficient as it can about that.
the important part of your problem does not likely lie in the internal/private implementation of worker threads. a good implementation will likely employ a thread pool because creating one thread per operation would cost a lot. operations can reuse and hold on to idle threads.
the important part (likely) lies in your use of the public apis of the implementation you have chosen.
one obvious implementation to support in this case is operation cancellation: -[NSOperation cancel]. when somebody navigates away from a view which has a pending/unfinished request, simply cancel it (unless you'll need the data for caching).
many implementations may also benefit by making requests less often. for example: if your server results only update about once per hour, then it doesn't make sense to request it 'about every minute'.
last point: a connection can use a worker thread itself - check the apis you are using to reduce this if it's a problem.
This question is for those of you who, unlike myself, truely understand multi-threading in cocoa apps. Here, briefly, is the situation:
Situation:
My app achieves concurrency by using the methods provided in NSObject. Please tell me if it is OKAY to do the following:
1) My main view controller launches some work in the background to free up the UI:
[self performSelectorInBackground:#selector(loadImages:) withObject:nil];
2) The background work divides its task into several smaller tasks on more background threads so that each task is updated as it finishes (as opposed to when all tasks finish):
[self performSelectorInBackground:#selector(loadOneImage:) withObject:nil];
Rationale:
This was the only way I could invent to get individual tasks (loading/drawing of custom UIViews) in a set to update in UI AS each is completed. Otherwise, all tasks only get updated when the last task in the group has completed...
yes, you may use performSelectorInBackground:... calls to spawn secondary threads from secondary threads.
if you have a lot threads to spawn (in this manner), consider an NSOperationQueue. Otherwise, you may end up with a ton of background threads. 100 threads (for example), each loading one image in a mobile device is not a good use of resources - nor will it be responsive. NSOperationQueue allows you to cap the max number of threads/workers, and reuses worker threads.
note: '100 threads' was used because the number is far beyond logical for the hardware (the question is tagged iPhone). if your image loading is all in memory, just use a serial (1 worker at a time) NSOperationQueue - NSOperations may specify priority. if images are being downloaded, then you may want to stick to 4 or less.
things are different on OS X, where there are more cores and resources available, so these numbers will change as the hardware platform change. on OS X, you can successfully use 100 threads in one app, although it is unusual to need anything near that many threads for most apps.
There's nothing wrong with this approach as far as I can see. According to the doc, performSelectorInBackground:withObject: just spawns another thread and executes your selector there. It doesn't list any limitations. Just don't forget to set up autorelease pools in each method that you call via performSelectorInBackground:withObject: to not leak any memory.
One critical condition you should make sure. Both threads which are running in background should not have any dependency. If they have, then you may end up in inconsistency.
So beter if you go for operation queues rather than spawning thread from another background thread.
For performance reasons, I instantiate a dedicated NSThread to process incoming messages that are streamed from a network server. I use an NSOperation for the purpose of instantiating the connection and receiving incoming data through the NSURLConnection delegates, but as soon as new data comes in and gets parsed, I offload the processing of the message to the dedicated NSThread. The idea is to let one thread focus on receiving incoming messages and let the other thread just do the processing.
What's the proper way to shut down the NSThread when the applicationDidEnterBackground comes in?
Also, how should I restart the NSThread when applicationWillEnterForeground comes in?
Other than the main thread, it seems the state of other background threads is not maintained between going to sleep and restarting.
By the way, I'm all for using NSOperations for most tasks that have a measurable amount of work -- ie, accessing a resource over the network, performing a calculation, etc. However, in this case, I need to process messages on the fly on a long-living dedicated thread that is always there by calling performSelector:onThread:withObject:waitUntilDone: and passing it the target thread. It seems NSOperation isn't a good fit for this.
I would appreciate your input.
"For performance reasons"?
If processing doesn't take much time, run everything (including NSURLConnection) on the main thread. Concurrency bugs are a major pain.
If you want things to run serially, you can emulate a "single thread" with an NSOperationQueue with maxConcurrentOperations = 1. I'm pretty sure that NSOperationQueue uses thread pools (and on 4.0, GCD, which probably uses thread pools), which means you don't need to keep a thread running all the time.
Apart from that, your process is automatically suspended and resumed by the system, so you don't need to kill off threads.
I'm not sure what you mean by "the state of other background threads".