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.
Related
I'm creating an application in objective C where I have two threads:
The main thread, which is woken up from sleep and is called into asynchronously by a module above it
The callback block(thread) whose execution is asynchronous and is dependent on an external module "M" sending a notification.
On my main thread, I want to wait for the callback to come in before I start doing my tasks. So, I tried using dispatch_group_enter and dispatch_group_wait(FOREVER) on the main thread while calling into dispatch_group_leave on the callback thread. This ensured that when the main thread is the first to execute, things happen as they are supposed to, i.e the main thread waits for the callback to come in and unblock it before performing its tasks.
However, I'm seeing a race condition where the callback block gets called first sometimes and is stuck on dispatch_group_leave (since at this point the main thread has not called into dispatch_group_enter.
Is there a different GCD construct I can use for this purpose?
The “main thread” is a thread which handles UI, system events, notifications, etc. We never block that thread. Blocking it results in a horrible UX where the app will appear to freeze and your app may even be terminated by the “watch dog” process, which kills apps that it thinks are frozen. In some cases, the app will deadlock.
So, if you really mean “main thread”, then the answer is that you would never “wait” on that thread (or otherwise block it). The pattern is to have your background thread do what it needs, and then dispatch model/UI updates back to the main thread with GCD (or submit your notification and let the main thread process it).
If you want a UX where the user is not allowed to interact with the UI while this background process is underway, you would present something in your UI that makes that clear. A common pattern is a dimming/blurring view that covers the whole view, often with a UIActivityIndicatorView (i.e., a spinner), and when the task dispatched to the background queue is done (or have the notification handler do that), you’d then remove that dimming/blurred view and the spinner and update the UI accordingly.
But you never block the main thread by waiting.
How to stop/cancel/suspend/resume tasks on GCD queue
How does one stop background queue operations? I want to stop some screens in our app. And some screens it should be auto resume. So, how does one pass a queue in iOS?
I mean when user have browsing the app time we run the background thread in dispatch_queue_t. But it never stops and resume in the code. So how does one suspend and resume a queue
To suspend a dispatch queue, it is simply queue.suspend() (dispatch_suspend(queue) in Objective-C). That doesn't affect any tasks currently running, but merely prevents new tasks from starting on that queue. Also, you obviously only suspend queues that you created (not global queues, not main queue).
To resume a dispatch queue, it is queue.resume() (or dispatch_resume(queue) in Objective-C). There's no concept of “auto resume”, so you'd just have to manually resume it when appropriate.
To pass a dispatch queue around, you simply pass the DispatchQueue object that you created (or the dispatch_queue_t object that you created when you called dispatch_queue_create() in Objective-C).
In terms of canceling tasks queued on dispatch queues, this is a was introduced in iOS 8. One can item.cancel() a DispatchWorkItem (dispatch_block_cancel(block) a dispatch_block_t object in Objective-C). This cancels queued blocks/items that have not started, but does not stop ones that are underway. If you want to be able to interrupt a dispatched block/item, you have to periodically examine item.isCancelled (or dispatch_block_testcancel() in Objective-C).
See https://stackoverflow.com/a/38372384/1271826 for examples on canceling dispatch work items.
If you want to cancel tasks, you might also consider using operation queues, i.e. OperationQueue (NSOperationQueue in Objective-C). Its cancelable operations have been around for a while and you're likely to find lots of examples online. It also supports constraining the degree of concurrency with maxConcurrentOperationCount (whereas with dispatch queues you can only choose between serial and concurrent, and controlling concurrency more than that requires a tiny bit of effort on your part).
If using operation queues, you suspend and resume by changing the suspended property of the queue. And to pass it around, you just pass the NSOperationQueue object you instantiated.
Having said all of that, I'd suggest you expand your question to elaborate what sort of tasks are running in the background and articulate why you want to suspend them. There might be better approaches than suspending the background queue.
In your comments, you mention that you were using NSTimer, a.k.a. Timer in Swift. If you want to stop a timer, call timer.invalidate() to stop it. Create a new NSTimer when you want to start it again.
Or if the timer is really running on a background thread, GCD “dispatch source timers” do this far more gracefully. With a GCD timer, you can suspend/resume it just like you suspend/resume a queue, just using the timer object instead of the queue object.
You can't pause / cancel when using a GCD queue. If you need that functionality (and in a lot of general cases even if you don't) you should be using the higher level API - NSOperationQueue. This is built on top of GCD but it gives you the ability to control how many things are executing at the same time, suspend processing of the queue and to cancel individual / all operations.
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.
I created this thread in my iOS app, and I'd like to stop it:
dispatch_queue_t myDispatch = dispatch_queue_create("com.myqueue", DISPATCH_QUEUE_CONCURRENT);
myDispatch thread within it invokes dispatch_global_queue and dispatch_main_queue respectively to execute heavy operations and to execute graphics operations.
But in response to a user action in the app can be called another function that uses another queue very similar to myDispatch. If myDispatch thread is terminated, there are no problems, but this call can also occur during the execution of myDispatch thread, and so my app crashes because both thread use the same arrays.
There is a way to stop or kill a thread before its termination? I'd like to kill the thread currently running and start the new thread.
If you want to cancel/stop background work you should be using NSOperation since as far as I know once you dispatch a block with GCD you lose control of it.
To cancel a GCD thread you have to use your own atomic flag.
I'm reading Core Animation Programming Guide and in the chapter of "Transactions", I see this
Important: When modifying layer properties from threads that don’t
have a runloop, you must use explicit transactions.
but From Apple's documentation on NSRunLoop
Your application cannot either create or explicitly manage NSRunLoop
objects. Each NSThread object, including the application’s main
thread, has an NSRunLoop object automatically created for it as
needed.
Doesn't it mean every thread has a runloop? or threads that's not created by NSThread, such as POSIX's pthread
It says “Each NSThread object, including the application’s main thread, has an NSRunLoop object automatically created for it as needed.”
If you don't do anything that tries to access a thread's run loop, the system won't create a run loop for the thread.
If you don't do [[NSRunLoop currentRunLoop] run] (or something equivalent), your thread won't run its run loop.
The UIApplicationMain function takes care of this for the main thread. For threads you create, you need to run the thread's run loop if you want the thread's run loop to have any effect.
Here's what's happening (I think) in the case of Core Animation, when you don't use an explicit transaction. It begins a transaction, and registers a callback with the current thread's run loop to commit it. (This will create a run loop for the current thread if necessary.) If you're not running the thread's run loop, that callback will never be called.