I have weird threading issues on iOS. Consider a simple method like this:
- (BOOL)doSomething
{
[self doA];
[self doB];
}
This method should only run in context of the main thread. I need to call this method from a worker thread. I use performSelectorOnMainThread for this purpose.
If I do everything as explained here. Can a context switch happen between doA and `doB?
(I don't think so, I just want to make sure that my understanding is right here)
By "context switch" I assume you mean the main thread switching to some other main thread event (as you could always switch to another worker thread at any time).
However, main thread will finish all of doSomething before doing anything else.
Excerpt from performSelectorOnMainThread:
This method queues the message on the run loop of the main thread
using the common run loop modes—that is, the modes associated with the
NSRunLoopCommonModes constant. As part of its normal run loop
processing, the main thread dequeues the message (assuming it is
running in one of the common run loop modes) and invokes the desired
method. Multiple calls to this method from the same thread cause the
corresponding selectors to be queued and performed in the same same
order in which the calls were made.
Related
In iOS, we have GCD and Operation to handle concurrent programming.
looking into GCD we have QoS classes, and they're simple and straight forward, this question is about why DispatchQueue.main.async is commonly used to asynchronies X tasks in the Main Thread.
So when we usually handle updating something in the UI we usually use that function since to prevent any irresponsiveness from the application.
makes me think is writing code inside the UIViewController usually executed in the main thread ?
but also knowing that callback & completionHandler usually execute without specifying on what thread they are in, and the UI never had a problem with that !! so it is on the background ?
How Swift handles this ? and what thread am i writing on by default without specifying anything ?
Since there are more than one question here, let's attempt to answer them one by one.
why DispatchQueue.main.async is commonly used to asynchronies X tasks
in the Main Thread.
Before mentioning a direct answer, make sure that you don't have confusion of understanding:
Serial <===> Concurrent.
Sync <===> Async.
Keep in mind that DispatchQueue.main is serial queue. Using sync or async has nothing to do with determining serialization or currency of a queue, instead they refer to how the task is handled. Thus saying DispatchQueue.main.async means that:
returns control to the current queue right after task has been sent to
be performed on the different queue. It doesn't wait until the task is
finished. It doesn't block the queue.
cited from: https://stackoverflow.com/a/44324968/5501940 (I'd recommend to check it.)
In other words, async means: this will happen on the main thead and update it when it is finished. That's what makes what you said:
So when we usually handle updating something in the UI we usually use
that function since to prevent any irresponsiveness from the
application.
seems to be sensible; Using sync -instead of async- will block the main.
makes me think is writing code inside the UIViewController usually
executed in the main thread ?
First of all: By default, without specifying which thread should execute a chunk of code it would be the main thread. However your question seems to be unspecific because inside a UIViewController we can call functionalities that are not executed on the main thread by specifying it.
but also knowing that callback & completionHandler usually execute
without specifying on what thread they are in, and the UI never had a
problem with that !! so it is on the background ?
"knowing that callback & completionHandler usually execute without specifying on what thread they are in" No! You have to specify it. A good real example for it, actually that's how Main Thread Checker works.
I believe that there is something you are missing here, when dealing when a built-in method from the UIKit -for instance- that returns a completion handler, we can't see that it contains something like DispatchQueue.main.async when calling the completion handler; So, if you didn't execute the code inside its completion handler inside DispatchQueue.main.async so we should assume that it handles it for you! It doesn't mean that it is not implemented somewhere.
Another real-world example, Alamofire! When calling
Alamofire.request("https://httpbin.org/get").responseJSON { response in
// what is going on here work has to be async on the main thread
}
That's why you can call it without facing any "hanging" issue on the main thread; It doesn't mean its not handled, instead it means they handle it for you so you don't have to worry about it.
I'm learning about blocks from a Stanford video. I'm now at the part which explains core data. The teachers mentions something about:
- (void)openWithCompletionHandler:(void (^)(BOOL success))completionHandler;
He said that completionhandler block will be called in the thread which called the method. So basically the method runs async but the blocks get called on the thread, lets assume main.
So my question is do all blocks run on the thread from where the method call was made. To illustrate why I ask this question, I have a Async class which does request to a server.
The format of all these methods is like this:
- (void) getSomething:(id <delegateWhatever> const)delegate{
goto background thread using GCD..
Got result from server...
Go back to main thread and call the delegate method...
}
When I use blocks I do not need to worry about going back to main thread if they will be called where the call was made?
Hope this is clear,
Thanks in advance
If something runs asynchronously, you should read a documentation to know on which thread, e.g. the completion block will be executed. If it is your code, you are in charge here, you can use global GCD queues, you can create your own queue and execute it there or whatever...
In general, blocks behaves like a function or a method call, it is executed on thread, which calls it. It is even possible that the same block will be executed from 2 different threads at the same time.
And just to be clear: Even if you are using blocks, you need to care about going back to main thread, of course if it is necessary
Nothing forces blocks to be called on a particular thread, so it depends on the specific method whether or not you need to worry about its callback being on the main thread. (In practice I don't remember ever seeing a library where a method called on the main thread would not call its completion handler also on the main thread. But you still need to read the documentation of the specific library and method you are using, as always.)
I call dispatch_async(dispatch_get_main_queue()from several background threads. However, it appears that occasionally the code in the dispatch block is not executed. Could this be because i dispatch asynchronously and the thread exits before the main queue can execute the code?
Have you tried putting an NSLog in the beginning of your code snippet to be absolutely sure that it's not executing? Sometimes an if statement with faulty logic will pre-terminate your code. (From my past experience ;])
The moment the dispatch_async() call returns, it's not important whether or not the thread that invoked it subsequently exits or not - the "request is in the system" so to speak! Something else is happening in those "occasional" cases. Does your program have a run loop or call dispatch_main() at the end of its main function? Not clear whether this is a Cocoa/iOS/POSIX application you're describing.
This is a two part question. Hope someone could reply with a complete answer.
NSOperations are powerful objects. They can be of two different types: non-concurrent or concurrent.
The first type runs synchronously. You can take advantage of a non-concurrent operations by adding them into a NSOperationQueue. The latter creates a thread(s) for you. The result consists in running that operation in a concurrent manner. The only caveat regards the lifecycle of such an operation. When its main method finishes, then it is removed form the queue. This is can be a problem when you deal with async APIs.
Now, what about concurrent operations? From Apple doc
If you want to implement a concurrent operation—that is, one that runs
asynchronously with respect to the calling thread—you must write
additional code to start the operation asynchronously. For example,
you might spawn a separate thread, call an asynchronous system
function, or do anything else to ensure that the start method starts
the task and returns immediately and, in all likelihood, before the
task is finished.
This is quite almost clear to me. They run asynchronously. But you must take the appropriate actions to ensure that they do.
What it is not clear to me is the following. Doc says:
Note: In OS X v10.6, operation queues ignore the value returned by
isConcurrent and always call the start method of your operation from a
separate thread.
What it really means? What happens if I add a concurrent operation in a NSOperationQueue?
Then, in this post Concurrent Operations, concurrent operations are used to download some HTTP content by means of NSURLConnection (in its async form). Operations are concurrent and included in a specific queue.
UrlDownloaderOperation * operation = [UrlDownloaderOperation urlDownloaderWithUrlString:url];
[_queue addOperation:operation];
Since NSURLConnection requires a loop to run, the author shunt the start method in the main thread (so I suppose adding the operation to the queue it has spawn a different one). In this manner, the main run loop can invoke the delegate included in the operation.
- (void)start
{
if (![NSThread isMainThread])
{
[self performSelectorOnMainThread:#selector(start) withObject:nil waitUntilDone:NO];
return;
}
[self willChangeValueForKey:#"isExecuting"];
_isExecuting = YES;
[self didChangeValueForKey:#"isExecuting"];
NSURLRequest * request = [NSURLRequest requestWithURL:_url];
_connection = [[NSURLConnection alloc] initWithRequest:request
delegate:self];
if (_connection == nil)
[self finish];
}
- (BOOL)isConcurrent
{
return YES;
}
// delegate method here...
My question is the following. Is this thread safe? The run loop listens for sources but invoked methods are called in a background thread. Am I wrong?
Edit
I've completed some tests on my own based on the code provided by Dave Dribin (see 1). I've noticed, as you wrote, that callbacks of NSURLConnection are called in the main thread.
Ok, but now I'm still very confusing. I'll try to explain my doubts.
Why including within a concurrent operation an async pattern where its callback are called in the main thread? Shunting the start method to the main thread it allows to execute callbacks in the main thread, and what about queues and operations? Where do I take advantage of threading mechanisms provided by GCD?
Hope this is clear.
This is kind of a long answer, but the short version is that what you're doing is totally fine and thread safe since you've forced the important part of the operation to run on the main thread.
Your first question was, "What happens if I add a concurrent operation in a NSOperationQueue?" As of iOS 4, NSOperationQueue uses GCD behind the scenes. When your operation reaches the top of the queue, it gets submitted to GCD, which manages a pool of private threads that grows and shrinks dynamically as needed. GCD assigns one of these threads to run the start method of your operation, and guarantees this thread will never be the main thread.
When the start method finishes in a concurrent operation, nothing special happens (which is the point). The queue will allow your operation to run forever until you set isFinished to YES and do the proper KVO willChange/didChange calls, regardless of the calling thread. Typically you'd make a method called finish to do that, which it looks like you have.
All this is fine and well, but there are some caveats involved if you need to observe or manipulate the thread on which your operation is running. The important thing to remember is this: don't mess with threads managed by GCD. You can't guarantee they'll live past the current frame of execution, and you definitely can't guarantee that subsequent delegate calls (i.e., from NSURLConnection) will occur on the same thread. In fact, they probably won't.
In your code sample, you've shunted start off to the main thread so you don't need to worry much about background threads (GCD or otherwise). When you create an NSURLConnection it gets scheduled on the current run loop, and all of its delegate methods will get called on that run loop's thread, meaning that starting the connection on the main thread guarantees its delegate callbacks also happen on the main thread. In this sense it's "thread safe" because almost nothing is actually happening on a background thread besides the start of the operation itself, which may actually be an advantage because GCD can immediately reclaim the thread and use it for something else.
Let's imagine what would happen if you didn't force start to run on the main thread and just used the thread given to you by GCD. A run loop can potentially hang forever if its thread disappears, such as when it gets reclaimed by GCD into its private pool. There's some techniques floating around for keeping the thread alive (such as adding an empty NSPort), but they don't apply to threads created by GCD, only to threads you create yourself and can guarantee the lifetime of.
The danger here is that under light load you actually can get away with running a run loop on a GCD thread and think everything is fine. Once you start running many parallel operations, especially if you need to cancel them midflight, you'll start to see operations that never complete and never deallocate, leaking memory. If you wanted to be completely safe, you'd need to create your own dedicated NSThread and keep the run loop going forever.
In the real world, it's much easier to do what you're doing and just run the connection on the main thread. Managing the connection consumes very little CPU and in most cases won't interfere with your UI, so there's very little to gain by running the connection completely in the background. The main thread's run loop is always running and you don't need to mess with it.
It is possible, however, to run an NSURLConnection connection entirely in the background using the dedicated thread method described above. For an example, check out JXHTTP, in particular the classes JXOperation and JXURLConnectionOperation
Imagine you want to do many thing in the background of an iOS application but you code it properly so that you create threads (for example using GCD) do execute this background activity.
Now what if you need at some point to write update a variable but this update can occur or any of the threads you created.
You obviously want to protect that variable and you can use the keyword #synchronized to create the locks for you but here is the catch (extract from the Apple documentation)
The #synchronized() directive locks a section of code for use by a
single thread. Other threads are blocked until the thread exits the
protected code—that is, when execution continues past the last
statement in the #synchronized() block.
So that means if you synchronized an object and two threads are writing it at the same time, even the main thread will block until both threads are done writing their data.
An example of code that will showcase all this:
// Create the background queue
dispatch_queue_t queue = dispatch_queue_create("synchronized_example", NULL);
// Start working in new thread
dispatch_async(queue, ^
{
// Synchronized that shared resource
#synchronized(sharedResource_)
{
// Write things on that resource
// If more that one thread access this piece of code:
// all threads (even main thread) will block until task is completed.
[self writeComplexDataOnLocalFile];
}
});
// won’t actually go away until queue is empty
dispatch_release(queue);
So the question is fairly simple: How to overcome this ? How can we securely add a locks on all the threads EXCEPT the main thread which, we know, doesn't need to be blocked in that case ?
EDIT FOR CLARIFICATION
As you some of you commented, it does seem logical (and this was clearly what I thought at first when using synchronized) that only two the threads that are trying to acquire the lock should block until they are both done.
However, tested in a real situation, this doesn't seem to be the case and the main thread seems to also suffer from the lock.
I use this mechanism to log things in separate threads so that the UI is not blocked. But when I do intense logging, the UI (main thread) is clearly highly impacted (scrolling is not as smooth).
So two options here: Either the background tasks are too heavy that even the main thread gets impacted (which I doubt), or the synchronized also blocks the main thread while performing the lock operations (which I'm starting reconsidering).
I'll dig a little further using the Time Profiler.
I believe you are misunderstanding the following sentence that you quote from the Apple documentation:
Other threads are blocked until the thread exits the protected code...
This does not mean that all threads are blocked, it just means all threads that are trying to synchronise on the same object (the _sharedResource in your example) are blocked.
The following quote is taken from Apple's Thread Programming Guide, which makes it clear that only threads that synchronise on the same object are blocked.
The object passed to the #synchronized directive is a unique identifier used to distinguish the protected block. If you execute the preceding method in two different threads, passing a different object for the anObj parameter on each thread, each would take its lock and continue processing without being blocked by the other. If you pass the same object in both cases, however, one of the threads would acquire the lock first and the other would block until the first thread completed the critical section.
Update: If your background threads are impacting the performance of your interface then you might want to consider putting some sleeps into the background threads. This should allow the main thread some time to update the UI.
I realise you are using GCD but, for example, NSThread has a couple of methods that will suspend the thread, e.g. -sleepForTimeInterval:. In GCD you can probably just call sleep().
Alternatively, you might also want to look at changing the thread priority to a lower priority. Again, NSThread has the setThreadPriority: for this purpose. In GCD, I believe you would just use a low priority queue for the dispatched blocks.
I'm not sure if I understood you correctly, #synchronize doesn't block all threads but only the ones that want to execute the code inside of the block. So the solution probably is; Don't execute the code on the main thread.
If you simply want to avoid having the main thread acquire the lock, you can do this (and wreck havoc):
dispatch_async(queue, ^
{
if(![NSThread isMainThread])
{
// Synchronized that shared resource
#synchronized(sharedResource_)
{
// Write things on that resource
// If more that one thread access this piece of code:
// all threads (even main thread) will block until task is completed.
[self writeComplexDataOnLocalFile];
}
}
else
[self writeComplexDataOnLocalFile];
});