I am writing an application that is going to send/receive data over tcp connections and I wanted to schedule the read/write to happen in the run loop of a different thread. Meaning Thread 1 is creating the connection and scheduling it on the run loop of Thread 2. I am unable to find any way of accessing the run loop of a different thread so I wrote a piece of code that the secondary thread will run which will store its run loop in a globally accessible location. I wanted to know if this is the right way to do it or if there is any other/better way to do the same and also if the way I have done it will cause problems like access to the run loop not being thread safe and causing issues if i attempt to schedule multiple things on the run loop of the same thread from multiple threads.
Something like the following.
[NSRunLoop currentRunLoop] --> This I can do from the thread whose runloop I want to access
NSRunLoop * secondthreadrunloop = [secondthread getRunLoop]; -->But is there anything like this?
I encountered the same problem recently and it seems that the answer is - no, you cannot schedule anything on a NSRunLoop running on a different thread. Apple says that NSRunLoop is not thread safe which means that attaching an NSTimer instance to it would result in an undefined behaviour (I have checked it, in my case it randomly generates crashes).
What can be done though is to schedule a repeating timer from the background thread itself and make it pick up the work you want it to do from some atomic property.
Related
Digging more than one day....Apple, Google, Slideshare and stackoverflow. But still not clear about NSRunLoop.
Every thread has a runloop by default.Application mainThread has mainRunLoop.
1. If MainRunLoop get input events is it creating new thread to execute it? Then another runLoop created? How then multiple thread and multiple runLoop work? Communicate?
2. If runLoop has no input event/task it sleeps.When a RunLoop ends?
3. Why i should care about runLoop?
4. Where i can use it?
Where i miss that i can't understand the life cycle?
Lets look on your`s list:
Wrong. Threads do not have built-in runloop. They need to be created manually.
Runloop doesn`t create another threads, its immediately executes an event. That is why at the main thread we can see locked interface - by heavy-load tasks in the main thread (UI in iPhone runs on the main thread). Runloops can communicate with each other with the help of mac ports.
Runloop sleeps before the first event come, then wakes up and ends. Only exception - timer, but it will not runloop. Runloop need to start run every time after Event (in the loop). If you call the run, there is already a built-in loop.
Can use to create some threads which must track or execute something periodically. For example, you can create a thread, when runloop for it and then other threads can execute it`s selectors through performSelector. This creates a background query processor, which does not require each time to create a new thread.
Is is possible in an iOS app to do the following:
Pause execution of a method running on the main thread.
Allow the main thread to continue, completing one loop of the main run loop (or continuing for a specified time period)
Resume execution of the previous paused method
?
I've searched and can't find anything that allows me to do this, but I have a feeling I've seen it done in the past by a programmer I previously worked with.
The motivation for this is the following:
I'm writing a test of a message routing class
The test (a) sends a message and then (b) analyses the outcome to determine if the test passed
The send message is sent using performSelectorOnMainThread:withObject:waitUntilDone:NO
There are 2 ways I can think to resolve this:
Split my test up and use performSelector:withObject:afterDelay:
Specify YES for waitUntilDone when sending the message
Both of these solutions are ok, but 1. complicates the test quite a lot, and 2. changes the messaging system I'm writing tests for, so will have to be carefully considered.
Considering what performSelectorOnMainThread:withObject:waitUntilDone:YES does, it seems like the functionality I'm asking for should be possible (as it's similar in many ways), but is it possible?
This might be what you're looking for:
[[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate date]];
This will execute one iteration of the main run loop and then continue on from there. This is kind of abusing the way the run loop is supposed to work though, so you might want to consider a different design - it's almost never a good idea to pause the main thread. Maybe do what you need to do in a separate thread and have that thread call back to a delegate method when it's done, or use a notification.
Use multithreading for this purpose. SENDER on the mainThread, WATCHER on a secondThread, what allows you to start WATCHING before SENDING.
The app I am working on fetches a bunch of different newsfeeds when it first starts up and updates any expired ones. While this is happening the interface often freezes up and you can't click anything. The actual network calls are being done on a separate thread, but the database operations are being done on the main thread. Would this cause the interface to freeze?
I have been told that I need to make it to where only two feeds to update are inserted into the network operation queue at a time so that it won't try all of them at once, but it's already set up to only do so many network calls at once. I don't understand how having less things in a queue at a time would cause it to go faster if they're just going to be put in there sequentially anyways. Please correct me if I am wrong, I'm still pretty new to this.
Any kind of help regarding what could cause the UI to freeze up during startup like this would be much appreciated!
It is always a good idea to move time consuming operation away from the main thread.
Fortunately it is pretty simple to do on iOS. If the time-consuming task is fairly simple you could consider using performSelectorInBackground
e.g:
[self performSelectorInBackground:#selector(myFunction:)
withObject:myParam];
It is however important to remberber, that you must not access the GUI from the background thread. To get objects back to the main thread use performSelectorOnMainThread
e.g:
[self performSelectorOnMainThread:#selector(myFunction:) myParamwaitUntilDone:YES];
Try applying this strategy to your database calls. Depending on your scenario you might want to wrap it up in a NSOperation or use a Thread when the cause of the freeze is found.
I am trying to understand multi-threading on iOS in more detail. I went through some of the class references like NSThread, NSRunLoop, NSTask..
First of all as indicated on the following link:
use of runloop
Runloop runs within a Thread.
So why do we need to define our own Runloop in our app? In the case of NSThread it is useful because some of time-consuming processes can run in a separate thread so that the app will still be responsive on the main thread.
Interacting with the thread's run loop may be useful if you have a thread whose work you want to continue periodically. That is, a run loop would do some work, and then when it is finished with that work, it would put the thread to rest for some time, then resume work at a later time -- effectively preventing the thread from exiting. You won't need to interact with them or configure/create them yourself regularly (only a small percentage of apps would qualify, if you are using high level abstractions such as Foundation because Foundation would set them up on your behalf in most scenarios).
If your secondary thread just does a specified task and does not need to wait for some external event (e.g. a download to finish), you would (typically) not need to interact with the run loop.
You might consider looking at using NSOperationQueues, NSOperations and NSBlockOperations instead as these will manage themselves, will allow for cancellation of tasks and can be scheduled on main and background threads.
I am working on an app, which uploads native contacts to server then get responses(JSON, a contact list that already installed the app). When native contacts are large enough, server response will be slow and unstable. And user cannot do other things. so I put network request into background thread. every time I will upload 100 contacts, do some tasks , then next 100 contacts until loop finish.
But in running, the result is not as expected. background thread is running, it keeps to request server. UI thread is blocked, I still cannot do anything.
is this cause a long loop in background thread? Although I have 2 thread, but they will compete CPU resources(test device is iPod, 1 core. And I think this may not related core numbers)?
Could anyone tell me hints on how to handle this kind of scenario? Thanks in advance!
Update:
I have found the root cause. A global variable in App delegate is set to wrong value, therefore UI behavior is weird. I found this by comment all network request method. So this problem is not related with multiple threading. Sorry for the bother.
I think there needs to be some clarification as to how you are performing the network operations.
1st, NSOperatiomQueue deals with NSOperations, so you are presumably wrapping your network code in an NSOperation subclass.
2nd, are you using NSURLConnections for your networking code?
3rd, is the blocking part the NSURLConnection or you delegate callback for NSURLConnection?
1 thing to note is that plain ol' NSURLConnections are implemented under the hood multithreaded. The object is placed into your main threads run loop by default (when run from the main thread), but the object is just a wrapper that handles callbacks to the delegate from the lower level networking code (BSD sockets) which happens on another thread.
You really shouldn't be able to block your UI with NSURLConnections on the main thread, unless A) you are blocking the thread with expensive code in the delegate callback methods or B) you are overwhelming your run loop with too many simultaneous URL connections (which is where NSOperationQueue's setMaxConcurrentOperationsCount: comes into play)