I have an application which uses some external library for analytics. Problem is that I suspect it does some things synchronously, which blocks my thread and makes watchdog kill my app after 10 secs (0x8badf00d code). It is really hard to reproduce (I cannot), but there are quite few cases "in the wild".
I've read some documentation, which suggested that instead creating another thread I should use run-loops. Unfortunately the more I read about them, the more confused I get. And the last thing i want to do is release a fix which will break even more things :/
What I am trying to achieve is:
From main thread add a task to the run-loop, which calls just one function: initMyAnalytics(). My thread continues running, even if initMyAnalytics() gets locked waiting for network data. After initMyAnalytics() finishes, it quietly quits and never gets called again (so it doesnt loop or anything).
Any ideas how to achieve it? Code examples are welcome ;)
Regards!
You don't need to use a run loop in that case. Run loops' purpose is to proceed events from various sources sequentially in a particular thread and stay idle when they have nothing to do. Of course, you can detach a thread, create a run loop, add a source for your function and run the run loop until the function ends. The same as you can use a semi-trailer truck to carry your groceries home.
Here, what you need are dispatch queues. Dispatch queues are First-In-First-Out data structures that run tasks asynchronously. In contrary to run loops, a dispatch queue isn't tied to a particular thread: the working threads are automatically created and terminated as and when required.
As you only have one task to execute, you don't need to create a dispatch queue. Instead you will use an existing global concurrent queue. A concurrent queue execute one or more tasks concurrently, which is perfectly fine in our case. But if we had many tasks to execute and wanted each task to wait for its predecessor to end, we would need to create a serial queue.
So all you have to do is:
create a task for your function by enclosing it into a Block
get a global queue using dispatch_get_global_queue
add the task to the queue using dispatch_async.
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
initMyAnalytics();
});
DISPATCH_QUEUE_PRIORITY_DEFAULT is a macro that evaluates to 0. You can get different global queues with different priorities. The second parameter is reserved for future use and should always be 0.
Related
How could I delay a background queue's execution, without using sleep? Further, how could I interrupt that delay if needs be?
The docs for RunLoop suggest a while loop around the function run with a custom condition in the while loop. But how would I setup a timer to toggle the while loops execution?
You can suspend custom dispatch queues (but not global queues nor main queue). That stops new tasks from starting on that queue, but it does not affect things already running on that queue. You can resume to start running items that had previously been dispatched to the queue, but had not yet started.
GCD also provides a native mechanism to cancel a particular work item, and dispatch it again later when you want execution to resume. Note that cancel does not perform preemptive cancellation, but rather only sets a Boolean, isCancelled, which your dispatched task would need to periodically check and manually exit.
(If you want to cancel tasks on a queue, you might consider OperationQueue, as that has more graceful cancelation capabilities than dispatch queues. Or you might consider the “structured concurrency” of async-await of Swift concurrency, which also has cancelation built-in.)
Now, while GCD does not have a notion of “suspending” a task dispatched to a background thread, you might be able to jury-rig something something with a very careful use a semaphores. But the details would vary greatly based upon your implementation, so it is hard to advise further without more details.
You asked:
The docs for RunLoop suggest a while loop around the function run with a custom condition in the while loop.
As a general rule, anything that involves spinning in a while loop is to be avoided. It is s very inefficient pattern and is to be avoided. Many years ago (e.g. before GCD, before URLSession, etc.), this spin-on-run-loop pattern was not unheard of (e.g., it was the go-to technique for running NSURLConnection on a background thread), but it is an anachronism nowadays. It is an inefficient approach; an anti-pattern.
Every Thread has its own RunLoop, how DispatchQueue interact with them? Is DispatchQueue using RunLoop to dispatch task to Thread or do it by another way?
Any thread can have a run loop, but, nowadays, in practice, only the main thread does.
When you create a thread manually, it will not have a run loop. When you call RunLoop.current, the name suggests that it is grabbing the thread’s run loop, suggesting that it always will have one. But in reality, when you call current, it will return the run loop if one is already there, and if not, it creates a RunLoop for you. As the docs say:
If a run loop does not yet exist for the thread, one is created and returned.
And if you do create a run loop, you have to spin on it yourself (as shown here; and that example is over-simplified). But we don’t do that very often anymore. GCD has rendered it largely obsolete.
At a high level, GCD has pools of worker threads, one pool per quality of service (QoS). When you dispatch something via GCD to any queue (other than targeting the main queue), it grabs an available worker thread of the appropriate QoS, performs the task, and when done, marks the worker thread as available for future dispatched tasks. No run loop is needed (or desired) for these worker threads.
The following senario was done using threads
A large queue #work_queue populated/enqueued by the main thread. Used Thread::Queue here.
≥ 2 connection objects of something are added in #conns which had to be loaded serially as part of the loading process uses Expect->spawn
Multiple Worker threads are invoked, and each thread given a single $conns[$i] object & reference to the shared \#work_queue.
The worker threads safely removes a single item from #work_queue and performs some processing through its connection object, after which it picks up the next available item from #work_queue.
When this #work_queue is empty all the threads will shutdown safely
Now, the problem is that the loading phase is taking too long in many cases. But due to the use of Expect->spawn, parallel loading of #conns is possible only on a separate process & not on a thread.
Please suggest a good way to achieve the above scenario using fork. Or, even better if there is a way to use Expect->spawn with threads. (UNIX/LINUX only)
See Is it possible to use threads with Expect?
I'm writing an app which requires running a method after another method completes. (Common scenario, right?)
I'm trying to implement chained methods. The best I've come up with is to call performSelector:withObject:afterDelay:. I'm simply not sure if that is the best way to do this. I've looked into how the Cocos2d game engine implements its CCSequence class, but I'm not sure I understand it.
I suspect blocks would do well here, except I'm not sure how to use them as callback objects or whatever.
How would I implement a mechanism for running methods, one after the other? (I'm open to using timers or blocks, but I don't know how I'd use blocks in this case.)
Edit:
To clarify, I'm trying to implement a system like cocos2d's CCSequence class, which takes a few methods and "dispatches" them in sequence. Things like animations, which take much more than a single clock cycle to run.
I'm not looking to block the main thread, nor do I want to hard code methods to each other. Cocos2d has a sequencing system where I can pass in methods to a queue and run them sequentially.
Edit 2:
Also, I'd like to be able to cancel my scheduled queues, and so I'm not sure GCD is a good match for this. Can GCD serial queues be canceled?
You can use the technique of Thread Migration
Then here comes the interesting task called GCD-Grand Central Dispatch
Grand Central Dispatch (GCD) is a technology developed by Apple Inc.
to optimize application support for systems with multi-core processors
and other symmetric multiprocessing systems.It is an implementation of
task parallelism based on the thread pool pattern.
GCD works by allowing specific tasks in a program that can be run in
parallel to be queued up for execution and, depending on availability
of processing resources, scheduling them to execute on any of the
available processor cores
Dispatch Queues are objects that maintain a queue of tasks, either anonymous code blocks or functions, and execute these tasks in their
turn. The library automatically creates several queues with different
priority levels that execute several tasks concurrently, selecting the
optimal number of tasks to run based on the operating environment. A
client to the library may also create any number of serial queues,
which execute tasks in the order they are submitted, one at a time.
Because a serial queue can only run one task at a time, each task
submitted to the queue is critical with regard to the other tasks on
the queue, and thus a serial queue can be used instead of a lock on a
contended resource.
Dispatch queues execute their tasks concurrently with respect to other
dispatch queues. The serialization of tasks is limited to the tasks in
a single dispatch queue.
In your case you can use Serial Dispatch Queues
Serial queues are useful when you want your tasks to execute in a
specific order. A serial queue executes only one task at a time and
always pulls tasks from the head of the queue. You might use a serial
queue instead of a lock to protect a shared resource or mutable data
structure. Unlike a lock, a serial queue ensures that tasks are
executed in a predictable order. And as long as you submit your tasks
to a serial queue asynchronously, the queue can never deadlock.
Unlike concurrent queues, which are created for you, you must
explicitly create and manage any serial queues you want to use. You
can create any number of serial queues for your application but should
avoid creating large numbers of serial queues solely as a means to
execute as many tasks simultaneously as you can. If you want to
execute large numbers of tasks concurrently, submit them to one of the
global concurrent queues. When creating serial queues, try to identify
a purpose for each queue, such as protecting a resource or
synchronizing some key behavior of your application.
dispatch_queue_t queue;
queue = dispatch_queue_create("com.example.MyQueue", NULL);
this code shows the steps required to create a custom serial queue.
The dispatch_queue_create function takes two parameters: the queue
name and a set of queue attributes. The debugger and performance tools
display the queue name to help you track how your tasks are being
executed. The queue attributes are reserved for future use and should
be NULL.
Grand Central Dispatch provides functions to let you access several
common dispatch queues from your application:
Use the dispatch_get_current_queue function for debugging purposes
or to test the identity of the current queue. Calling this function
from inside a block object returns the queue to which the block was
submitted (and on which it is now presumably running). Calling this
function from outside of a block returns the default concurrent queue
for your application.
Use the dispatch_get_main_queue function to get the serial
dispatch queue associated with your application’s main thread. This
queue is created automatically for Cocoa applications and for
applications that either call the dispatch_main function or configure
a run loop (using either the CFRunLoopRef type or an NSRunLoop object)
on the main thread.
Use the dispatch_get_global_queue function to get any of the
shared concurrent queues.
Note: You do not need to retain or release any of the global dispatch
queues, including the concurrent dispatch queues or the main dispatch
queue. Any attempts to retain or release the queues are ignored.
Source: Concurrency Programming Guide
What about using a serial GCD queue?
private dispatch queues
Serial queues (also known as private dispatch queues) execute one task at a time in the order in which they are added to the queue. The currently executing task runs on a distinct thread (which can vary from task to task) that is managed by the dispatch queue. Serial queues are often used to synchronize access to a specific resource.
You can create as many serial queues as you need, and each queue operates concurrently with respect to all other queues. In other words, if you create four serial queues, each queue executes only one task at a time but up to four tasks could still execute concurrently, one from each queue. For information on how to create serial queues, see “Creating Serial Dispatch Queues.”
(source)
This would be useful if you want that all of your messages be handled in a background thread.
There are two performSelector method that can wait for completion, no need to guess a timing.
[self performSelector:<#(SEL)#> onThread:<#(NSThread *)#> withObject:<#(id)#> waitUntilDone:<#(BOOL)#>];
[self performSelectorOnMainThread:<#(SEL)#> withObject:<#(id)#> waitUntilDone:<#(BOOL)#>];
It sounds like you want to check out NSOperationQueue, NSOperation, and either NSBlockOperation or NSInvocationOperation. Unlike a GCD queue, an NSOperationQueue supports cancelling jobs.
You can create your own queue and set its maximum concurrent operation count to 1 to force it to execute operations serially. Or you can set dependencies between operations to force those operations to run serially.
Start with the chapter on Operation Queues in the Concurrency Programming Guide.
I finally found what I'm looking for. Completion blocks. Simply put, I'd write a method like this:
- (void) performSomeActionWithCompletion:(void (^)()) completion{
[self someAction];
if(completion()){
completion();
}
}
Now I can call my method like so:
[self performSomeActionWithCompletion:^{
NSLog(#"All done! (Well, not the async stuff, but at any rate...)");
}];
Let's say I have 2 threads, one is the main thread and another one, a secondary thread. The main thread is being used the most, but sometimes (rarely) I want the secondary thread to do some work based on calls from the main thread. Most of the time the secondary thread should sleep. Now after some searching I understand the way to do this is to use runLoops. So I tried to read apple's docs (http://developer.apple.com/library/ios/#documentation/Cocoa/Conceptual/Multithreading/RunLoopManagement/RunLoopManagement.html#//apple_ref/doc/uid/10000057i-CH16-SW5)
but it looks to me very complex and I'm having some hard time there. Is there an elegant and simple way to achieve what I described? Any similar runLoop code examples out there that I can run and play with?
Thanks
Each thread has a run loop.
Each run loop has a list of things that need to be done. These things are said to be “scheduled” on the run loop, although not all of them are scheduled for a specific date and time:
Timers are.
Sources aren't. They generally wait for something to come knocking at a Mach kernel port or a file descriptor.
When the run loop is running, it's usually not running—that is, the thread is sleeping, not consuming any CPU cycles. (If you sample it, you'll find the process appearing to be stuck in mach_msg_trap. This is the “wait-for-something-to-happen” system call.) The kernel wakes up the thread (which thereby returns from mach_msg_trap) when something happens that the thread's run loop needs to take care of.
The way to do exactly what you described is to implement a run loop source. You schedule the source on the secondary thread's run loop, implement it by doing work, and signal it from the primary thread when there's work to be done.
However, NSOperation is almost certainly a better solution, as it's designed for the case you described: Discrete units of work that need to be done serially, up to N (which you choose and is at least 1) at a time.
Note that NSOperationQueue reuses threads, so it does not necessarily create a new thread for every operation. Indeed, not doing that is part of the point: It creates the threads lazily, and uses any that it already has that aren't doing anything.
This sounds like just the sort of thing NSOperation/NSOperationQueue was made for. If you only have the occasional "units of work", why not make them an operation, then monitor it for completion and update your UI accordingly?
Matt Gallagher has a nice blog article comparing the secondary thread approach with other ways of getting background work done.
http://cocoawithlove.com/2010/09/overhead-of-spawning-threads.html
In your case, you don't have to be concerned with thread-creation overhead. But Matt's code examples might provide some insight into managing the secondary thread's runloop.
All that said, I would go with Joshua's advice and just use an NSOperationQueue and an NSOperation to do the background work. If the work could be encapsulated in an NSInvocation, you can use an NSInvocationOperation and avoid an NSOperation subclass.