EDIT:
Today I worked with my code a bit and gave background thread task some advantages over the main thread task. Essentially one thread is working with the even numbered items in an array and the other is working with the odd numbers. I gave the background thread the even number half, which will be either equal to or one less than the odd half. Also, I moved the while(!collisionDone) to a bit later in the code, as far back as I can to keep it thread safe. I put an NSLog in there to detect if the condition is ever false when it reaches that point, and it has not fired once. Furthermore, the instruments build now runs fine. That means the problem is stalling the main thread with the while loop. So that means my question now is:
How should I stall the main thread to wait until the background thread completes a task? Perhaps demonstrate how one uses NSLock to achieve that functionality?
Today I added multithreading in an attempt to reduce lag in my game. I am using an NSTimer to call my time loop function like this:
[NSTimer scheduledTimerWithTimeInterval:0.03 target:self selector:#selector(time:) userInfo: nil repeats: true];
Then, in that function, I have:
//run collision using multithreading
collisionDone = false;
[self performSelectorInBackground:#selector(collideBackground:) withObject:objectToSend];
[self collideMain:objectToSend];
while (!collisionDone) {
//notdone
}
The idea is that I am running one set of collision checks on one thread and another set in another thread. The empty while loop is to ensure the background thread has completed before carrying on in the function. After this bit, the code goes on to update the view.
This all runs fine, normally, but when I went to Instruments to check on my FPS, I found that the game freezes, it appears that it is freezing somewhere in the first run of the time loop. The app ran fine in Instruments before the multithreading, which essentially worked the same, except simply using [self collideBackground:objectToSend] rather than sending it to another thread. It would appear that the background thread is not completing or not being run at all, so collisionDone will always be false, and therefore the app will wait in that endless loop until I kill it.
Also, I have tried replacing that line with [self collideBackground:arrayToSend];, and it starts working again. Another thing I tried is putting NSLog(#"called"); at the very start of viewWillAppear: and it seems to run exactly one more frame!?!?!?
I would like any ideas as to why exactly this is happening and how to fix it. I doubt it is unrelated to the multithreading as doing everything on a single thread fixes it.
Yes it is related to multithreading, and your poll on the main thread. You should put a callback into your background thread instead. And not blocking the main thread.
// is not good at all
collisionDone = false;
/* etc... that will make collision to true */
while (!collisionDone) {
}
make instead
[self computeSomethingOnBagroundWhithData: nil onCompletion:^{
// callBack stuff
}]
Edit:
while (!collisionDone)
is always true because when you are creating another thread, the instruction is stuck in "while" statement, and is not reevaluating each time "collisionDone" as you would expect, but evaluate it inside the while scope. By the way, there could be some optimizer stuff, and "collisionDone" could only be evaluated in local scope (but here I'm only guessing, since it's compiler dependent). You should reenter the method in order to make it work, or join the thread when you are done. Since you don't want complex code for this kind of stuff, block are exactly what you want. libDispatch are perfect for this.
So you can try "self.collisionDone" in order to try to get the genuine pointer. But there's lot of chance that won't change anything.
Or you can do your stuff in background and when you are done, send a signal / callback to your client code. You don't need to make polling like you did. This is really bad design (really trust me ;) )
It has been unclear what the best approach to solving this problem or what the actual cause of the problem (yes it is that while(!collisionDone) loop, but why?), but none of the proposed alternatives can do the job properly. Although other options have been suggested, NSThread and performSelectorInBackground: have been the only ways I have detected the functionality I am looking for. Furthermore, after testing with the multithreading part turned off, I realized that the iPhone 4s has a much, much better processor than the iPhone 4, and I can run the game at 60 FPS on the iPhone 4s and newer without multithreading (the iPhone 4 can only handle 30 FPS). Lastly, I intended for my multithreading to utilize the dual-core processor of the iPhone 4s, so half of the collision would be done in one core while the other core does the other half. I am not sure this is actually happening and I have no idea how to detect this.
My conclusion: Remove this attempt at multithreading from the app completely. It is unnecessary and the benefits are dubious.
#Fonix: The question you linked to shows exactly what I am trying to do, however, none of the solutions they presented seem to be working.
The final solution after talking to my programmer friend about it at school:
//threadLock is an NSLock declared in the .h
-(void)timeLoop {
[self performSelectorInBackground:#selector(collideBackground) withObject:nil];
[self collideMainAtStartingPoint:0];
[threadLock lock];
[threadLock unlock];
//update the view hierarchy and stuff like that
}
-(void)collideBack {
[threadLock lock];
[self collideMainAtStartingPoint:2];
[threadLock unlock];
}
Related
I'm currently writing an iOS app in Swift, and I encountered the following problem: I have an object A. The problem is that while there is only one thread for the app (I didn't create separate threads), object A gets modified when
1) a certain NSTimer() triggers
2) a certain observeValueForKeyPath() triggers
3) a certain callback from Parse triggers.
From what I know, all the above three cases work kind of like a software interrupt. So as the code run, if NSTimer()/observeValueForKeyPath()/callback from Parse happens, current code gets interrupted and jumps to corresponding code. This is not a race condition (since just one thread), and I don't think something like this https://gist.github.com/Kaelten/7914a8128eca45f081b3 can solve this problem.
There is a specific function B called in all three cases to modify object A, so I'm thinking if I can make this function B atomic, then this problem is solved. Is there a way to do this?
You are making some incorrect assumptions. None of the things you mention interrupt the processor. 1 and 2 both operate synchronously. The timer won't fire or observeValueForKeyPath won't be called until your code finishes and your app services the event loop.
Atomic properties or other synchronization techniques are only meaningful for concurrent (multi-threaded) code. If memory serves, Atomic is only for properties, not other methods/functions.
I believe Parse uses completion blocks that are run on a background thread, in which case your #3 **is* using separate threads, even though you didn't realize that you were doing so. This is the only case in which you need to be worried about synchronization. In that case the simplest thing is to simply bracket your completion block code inside a call to dispatch_async(dispatch_get_main_queue()), which makes all the code in the dispatch_async closure run on the main, avoiding concurrency issues entirely.
If a second method is called from a method that is running on a background thread, is the second method automatically ran in that same thread or does it happen back on the main thread?
Note: I want my second method to be handled in the background, but since I update the UI inside it, would doing the following, be the right way to do it:
- (void)firstMethod {
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
//Do stuff in background
...
//Call a second method (Assume it'll run in this background thread as well)
[self secondMethod];
});
}
//Second Method
- (void)secondMethod {
//Do heavy lifting here
...
dispatch_async(dispatch_get_main_queue(), ^{
//Update UI here
...
});
}
Update
Oh I totally forgot to mention that this method is something that loads suggestions into the view (think keyboard suggestions). Since every key tap would be calling this method, I only want it to run when a user has finished typing. The way I'm approaching it is by setting a 0.2 delay between keys and if a new key tap fall within that 0.2 delay it cancels the previous method call, and initiates a new call (this way, assuming the use types the word, "the", it doesn't run suggestions for "t", "th", "the". Since the user is typing pretty quickly we can assume they don't want suggestions for anything until they have stopped typing (allowing the call to go through after a 0.2s delay), or if they type slow (where they probably are looking for suggestions along the way).
So when calling my secondMethod I do the following:
[NSObject cancelPreviousPerformRequestsWithTarget:self selector:#selector(secondMethod) object:nil];
[self performSelector:#selector(secondMethod) withObject:nil afterDelay:0.2];
The problem is it's not being called (I'm assuming this method defaults it to be performed in the main thread?)
Generally speaking, nothing is going to hop between threads without being pretty explicit about it. Certainly something as trivial as just calling a method isn't. Your code seems fine. Remember to not access mutable state from more than one queue at once (for example if the heavy lifting uses instance variables, make sure that -firstMethod doesn't get called twice in a row. It'd spawn off two async calls to -secondMethod then, and they'd step all over each others data. If that's a problem, create a serial dispatch queue instead of using a global one).
Essentially, I have a set of data in an NSDictionary, but for convenience I'm setting up some NSArrays with the data sorted and filtered in a few different ways. The data will be coming in via different threads (blocks), and I want to make sure there is only one block at a time modifying my data store.
I went through the trouble of setting up a dispatch queue this afternoon, and then randomly stumbled onto a post about #synchronized that made it seem like pretty much exactly what I want to be doing.
So what I have right now is...
// a property on my object
#property (assign) dispatch_queue_t matchSortingQueue;
// in my object init
_sortingQueue = dispatch_queue_create("com.asdf.matchSortingQueue", NULL);
// then later...
- (void)sortArrayIntoLocalStore:(NSArray*)matches
{
dispatch_async(_sortingQueue, ^{
// do stuff...
});
}
And my question is, could I just replace all of this with the following?
- (void)sortArrayIntoLocalStore:(NSArray*)matches
{
#synchronized (self) {
// do stuff...
};
}
...And what's the difference between the two anyway? What should I be considering?
Although the functional difference might not matter much to you, it's what you'd expect: if you #synchronize then the thread you're on is blocked until it can get exclusive execution. If you dispatch to a serial dispatch queue asynchronously then the calling thread can get on with other things and whatever it is you're actually doing will always occur on the same, known queue.
So they're equivalent for ensuring that a third resource is used from only one queue at a time.
Dispatching could be a better idea if, say, you had a resource that is accessed by the user interface from the main queue and you wanted to mutate it. Then your user interface code doesn't need explicitly to #synchronize, hiding the complexity of your threading scheme within the object quite naturally. Dispatching will also be a better idea if you've got a central actor that can trigger several of these changes on other different actors; that'll allow them to operate concurrently.
Synchronising is more compact and a lot easier to step debug. If what you're doing tends to be two or three lines and you'd need to dispatch it synchronously anyway then it feels like going to the effort of creating a queue isn't worth it — especially when you consider the implicit costs of creating a block and moving it over onto the heap.
In the second case you would block the calling thread until "do stuff" was done. Using queues and dispatch_async you will not block the calling thread. This would be particularly important if you call sortArrayIntoLocalStore from the UI thread.
iOS's UIKit is not thread-safe, let us call this fact well known. I know the rule, I'm careful, but I still get bitten - and every now and then the resulting crash is far enough removed from the offending background call into UIKit to make tracking down the problem a less than joyeus experience.
This problem seems like it could be easy to solve - have UIKit classes/methods warn when they are invoked from a background thread, at least as a debug feature. As far as I'm aware, iOS does not provide any such feature. Of course one could achieve the same effect manually by having some form of assertions precede such calls, but this solution is not the most elegant and in addition suffers from the same weakness as the original problem, namely that programmers are prone to forgetfulness.
Does anyone have a more elegant solution? How do you deal with this problem in your projects?
(Note: this question is related, but not quite as explicit. One is left wondering)
UPDATE: Andrew's answer is the solution I was looking for at the time, however note that at least as of Xcode 9 this is now provided by xcode/ios. For instance, adding this code:
DispatchQueue.global().async {
print(self.view.frame)
}
To a UIView's viewDidLoad method produces a runtime warning inline in Xcode UIView.frame must be used from the main thread only and a message printed to the console: Main Thread Checker: UI API called on a background thread: -[UIView frame]
This code (just add to project and compile this file without ARC) causes assertions on UIKit access outside of the main thread: https://gist.github.com/steipete/5664345
I've just used it to pickup numerous UIKit/main thread issues in some code I've just picked up.
I try not to introduce multi threading unless I have tried a single threaded approach first but it depends on the problem you are trying to solve.
Even when multithreading is the only option I usually avoid long running background operations or operations that that perform several unrelated tasks.
Just my opinion.
Edit
An example of doing work on the main thread whilst displaying a loading spinner:
MBProgressHUD *hud = [MBProgressHUD customProgressHUDInView:view dim:dim];
[hud show:NO];
//Queue it so the ui has time to show the loading screen before the op starts
NSBlockOperation *blockOp = [NSBlockOperation blockOperationWithBlock:block];
NSBlockOperation *finOp = [NSBlockOperation blockOperationWithBlock:^{
[MBProgressHUD hideAllHUDsForView:view animated:NO];
}];
[finOp addDependency:blockOp];
[[NSOperationQueue mainQueue] addOperations:#[blockOp, finOp] waitUntilFinished:NO];
Personally anytime I open the box to a multithreaded approach I start wrapping ALL interface based calls in performSelectorOnMainThread: so that there is never an issue. If we've already made it to the main there this call shouldn't cause any significant slow down, but if its called from a background thread I can rest easy knowing its safe.
[NSThread detachNewThreadSelector:#selector(addressLocation:) toTarget:self withObject:parameter];
[self addressLocation:parameter];
Should these two statements do the same thing? Because one of them (the second one) gives me an accurate result, and the other consistently gives me a random location off the coast of Africa. From what I have read, they should both do the same thing; execute addressLocation with the argument 'parameter.' The only difference is the thread, but it is accessing a global volatile variable, so that shouldn't matter, should it?
Threads are much more complicated than that. When you call detachNewThreadSelector, you are creating a new thread, but there's no simple way for you to know when that call completes. It could complete before the next line of code in the calling thread or many seconds later.
If you create the thread first, you can then use performSelector:onThread:withObject:waitUntilDone and you should get the same result as if you used [self addressLocation:parameter]. That won't do you a lot of good though because your main thread will be doing nothing while you wait for the result.
There are lots of ways to get data back from a thread -- I like to call performSelectorOnMainThread from the secondary thread to send the data back to the main thread, for example.
I would read up on Grand Central Dispatch to see if it suits your needs.