Maybe this question has been asked before in a different form. But I think I am looking at this differently. I am doing some optimisation task for the entire codebase and lots of refactoring and that would server the readability of the code also.
So I found [NSThread sleepForTimeInterval:] method written somewhere and for the delays I always use the GCD's dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(<#delayInSeconds#> * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{}) method. So the question is which is better?
There's a huge difference.
[NSThread sleepForTimeInterval:]
blocks the current thread. This is terrible if the current thread is the main thread. And it may be fine or terrible on other threads depending on what you are doing.
dispatch_after
doesn't block the current thread. The block is queued up to run at a future time on the designated queue while the current thread continues on its merry way.
Without any specific context, I would say that dispatch_after is the much better approach in almost all cases.
When considering replacing uses of [NSThread sleepForTimeInterval:] with GCD's dispatch_after, there are a couple of things to consider. As #rmaddy said, [NSThread sleepForTimeInterval:] blocks the thread on which it's called. As pointed out, this would be very bad for the main thread, and more generally any thread with a run loop on it (because the run loop will not iterate during that call.)
If you have a thread with a run loop on it, you can "spin" the runloop for some amount of time, which allows other run loop sources to remain responsive while the calling thread of execution remains effectively blocked. Details of spinning a run loop are beyond the scope of the question, but using run loops is yet another way you could solve this problem (although GCD is probably still preferred if you're not pushed into using run loops by some other API that relies on them like, for instance, NSStream.)
The primary high-level reason to avoid blocking threads with [NSThread sleepForTimeInterval:] is that threads are a finite resource. If you were to make a loop spawning many threads, all of which subsequently became blocked by virtue of calling [NSThread sleepForTimeInterval:], you would eventually be unable to create more threads. Thread exhaustion isn't a problem with particularly elegant solutions in the framework, because in general, it's a situation that the framework expects you to avoid, at an architectural level, by using a better abstraction like GCD or run loops.
One other situation where [NSThread sleepForTimeInterval:] will differ substantially from dispatch_after is when there is thread-local storage in play. If your code has done something like [[NSThread currentThread] threadDictionary][#"foo"] = #"bar"; then after [NSThread sleepForTimeInterval:] returns, you will be on the exact same thread, and therefore the value you had put into thread-local storage will still be there. With dispatch_after there is no guarantee that your block will run on the same thread from which it was enqueued. (And the overall likelihood of that happening by chance is fairly low as well in a process with other background tasks in flight.) In general, it's not a great idea to use TLS with GCD. That's generally OK because the ability to capture variables in block closures can solve many of the problems that people have historically used TLS to solve.
Thread-local storage is one of those things that can be extremely useful when it solves a given problem, but can make code brittle in the face of refactorings like this. What's worse is that you might not be able to know that other parts of the code are relying on TLS except by seeing things fail (or act weird).
Long story short, there's not enough info here to say which mechanism is best for your situation. If one were starting from scratch though, I struggle to think of a reason one would choose to block a thread using [NSThread sleepForTimeInterval:] instead of using a more modern deferral mechanism.
Related
In my daily work, I can handle some multi-thread condition using GCD, but I also see the usage of thread in project, example, log module use thread but queue in my project. I just can't understand why. There is some benefit to use thread than GCD? How should I choose?
pthread is often used by cross-platform code, so you are quite likely to run into it when you include third-party libraries in your code, and usually don't touch it.
Using NSThread would be very, very, very rare. Except for calling [NSThread isMainThread] to determine whether your code is running on the main thread or on a background thread. Unless you have a real good reason to do otherwise, use GCD. Mostly because it is so much easier to use that suddenly you will use multithreading in places where you would never have used it otherwise.
I'd worked on Java, and pretty much clear with the working of threads and thread pool.
I was wondering if anyone can explain the working of how thread's are created and allocated space in the thread pool in swift ?.
Also, does
Dispatch.main.async {
// some code
}
Creates a new Thread or Asynchronously executes the task ?
Thanks in advance =)
Queues and threads are separate concepts. Queues are ordered (sometimes prioritized) sequences of blocks to execute. As (mostly) an implementation detail, blocks must be scheduled onto threads in order to execute, but this is not the major point of them.
So Dispatch.main.async dispatches (appends) a block to the main queue. The main queue is serial and somewhat special in that it is promised to also be run exclusively on the main thread (as noted by Paulw11). It also promises to be associated with the main runloop. Understanding this "appends a block to a queue" concept is critical, because it has significant impact on how you design things in queues vs how you design things in threads. async does not mean "start running this now." It means "stick this on a queue, but don't wait for it."
As a good example of how the designs can be different, placing something on a queue doesn't mean it will ever run (even without bugs or deadlocks). It is possible and useful to suspend queues so that it stops scheduling blocks. It's possible to tie queues to other queues so that when a queue "schedules" something, it just puts it onto another queue rather than executing it. There are lots of things you can do with queues unrelated to "run things in the background." You can attach completion handlers to blocks. You can use groups to wait on collections of blocks. GCD is a way of thinking about concurrency. Parallelism is just a side benefit. (A great discussion of this concept is Concurrency is not parallelism by Rob Pike. It's in Go, but the concepts still apply.)
If you call Dispatch.main.async while running on the main queue, then that block is absolutely certain to not execute until the current block finishes. In UIKit and AppKit, "the current block finishes" often means "you return from a method that was called by the OS." While not implemented this way, you can pretend that every time you're called from the OS, it was wrapped in a call to Dispatch.main.async.
This is also why you must never call Dispatch.main.sync (note sync) from the main queue. That block will wait for you to return, and you'll wait until the block finishes. A classic deadlock.
As a rule, the thread pool is not your business in iOS. It is an implementation detail. Occasionally you need to think about it for performance reasons, but if you are thinking too much about it, you probably are designing your concurrency incorrectly.
If you're coming from Java, you definitely want to read Migrating Away From Threads in the Concurrency Programming Guide. It's the definitive resource for how to rethink thread-based patterns in queues.
Your code queues the block of code on the main queue (Dispatch.main) and returns immediately (.async), before executing the code.
You do not have control over which thread is used by the queue. Even if you create an own queue:
let serialQueue = DispatchQueue(label: "queuename")
serialQueue.async {
...
}
you do not know which thread your code will be running on.
Update:
As correctly stated by Paulw11 in the comment,
... if you dispatch a task on the main queue, it is guaranteed to execute on the main thread. If you dispatch a task on any other queue, you don't know which thread it will execute on; it may execute on the main thread or some other thread.
Recently, i am learning concurrency in swift. According to apple's document in NSOperation class reference :
When you add an operation to an operation queue, the queue ignores the value of the asynchronous property and always calls the start method from a separate thread. Therefore, if you always run operations by adding them to an operation queue, there is no reason to make them asynchronous.
does it mean synchronous in a separate thread is the same as asynchronous? and when i do the test with the following code, the operation indeed doesn't block the current main thread.
let operationQueue = NSOperationQueue()
let operation = NSBlockOperation(){
//do some task here
}
operationQueue.addOperation(operation)
so if it is true, then why should we create concurrency subclass of NSOperation?
Oh, NSOperation. Such a bizarre history you have.
NSOperation is relatively old (in iOS terms; fairly modern in ObjC terms). It was added in OS X 10.5. Before OS X 10.6/iOS 4, there were no NSBlockOperation objects. There were no blocks at all. So the only way to make an operation was to subclass or use NSInvocationOperation. Both approaches are cumbersome, but were still easier and more powerful than the older approach of using NSThread directly.
(This was right at the time when multi-core became a thing. 10.5 was famous for adding Core Animation which was I believe the first major preemptive multitasking framework in Cocoa. Before 10.5, most things were done with the runloop and cooperative multitasking, which is actually very efficient and effective for single-core systems. But it doesn't scale well to multi-core systems. Tools like NSOperation were provided to help us write better multi-core code, but GCD was so much more powerful that it completely dominated how multitasking code is written in Cocoa.)
When you subclass NSOperation, you needed to tell the system whether your operation is asynchronous. This isn't a request to run you asynchronously. This is a promise that your start method will not block. It's up to your start method to make sure the operation really is asynchronous.
This is only necessary in the case that your NSOperation is being started manually, and even then it was often not needed. If you put it onto an NSOperationQueue (and you really should always do that), this property is irrelevant. I remember it creating a lot of confusion at the time.
It's become even more irrelevant since the introduction of blocks. It is almost always much easier to use an NSBlockOperation (or dispatch_async) than to subclass NSOperation, which was always a bit tricky to get quite right.
Just in case you haven't already read it, if you want to study Cocoa concurrency, you definitely want to start with the Concurrency Programming Guide.
Asynchronous is always defined relative to the thread that makes a request. So a request is asynchronous relative to thread A if thread A makes a request that runs in thread B such that thread A is able to do other work while thread B is running the request.
If thread B in turn farms out the request to thread C such that thread B is able to do other work while thread C is running the request then that second request is an asynchronous relative to thread B.
It doesn't make sense to just keep farming out the same element of work asynchronously over and over again, of course. But assume the work delegated by thread A to thread B described above can be split up into multiple smaller elements of work. It would be reasonable for thread B to invoke those smaller elements of work asynchronously on threads C, D, etc. This might happen if B provides a service to A such that A doesn't want/need to know the details of how the work gets done; it just wants the work done asynchronously. B knows the details and can decide if/how to accomplish the work via smaller parallel units.
I'm developing a sort of fast image scan application.
In the -captureOutput:didOutputSampleBuffer:fromConnection: method I pick up the CVPixelBuffer and add them into an NSOperation subclass. The NSOperation takes the buffer and transform it into an image saved on the filesystem.
The -isConcurrent method of NSOperation returns YES. After the operation is created, is added to an NSOperationQueue.
Everything runs fine except for one issue that is affecting the scan frame rate.
Using time profiler I've found that some of the NSOperation are run on the same thread of the AVCaputureVideoOutput delegate that I've created:
dispatch_queue_t queue = dispatch_queue_create("it.CloudInTouchLabs.avsession", DISPATCH_QUEUE_SERIAL);
[dataOutput setSampleBufferDelegate:(id)self queue:queue];
When an operation is running on the same thread of the AV session queue, it affects the frame rate, it happens only with some them, probably GCD is deciding under the hood dispatching on active threads. The only method I've found to solve that issue, is to create a separate thread and pass it to the the single operations forcing them to run on it.
Is there another way, to force operations run on a different thread?
[EDIT]
Following the #impcc suggestions I made some test.
Results are pretty interesting even if sort of inconsistent.
Test platform is an iPhone 5 connected in debugging mode through an MBP with 16gb of RAM quad core i7. The session has a 60fps output tested with the algorithm in RosyWriter apple sample code.
AVSession queue sync targeted to high priority queue
26 fps where sometimes the thread of the queue is shared with the one
of the operation. The time taken inside the delegate methods has an
average of 0.02s
14 fps with a thread created just for the operations, if the main
method is not called on this thread, the start will be forced to perform
on that specific thread. This thread is create one time and kept
alive with a dummy port.. The time taken inside the delegate is
0.008.
AVSession queue concurrent targeted to high priority queue
13.5 fps where sometimes the thread of the queue is shared with the one of the operation. The time taken inside the delegate methods has
an average of 0.02s, substacially equal to the counterpart with sync
queue.
14 fps with a thread created just for the operations, if the main
method is not called on this thread, the start will be forced to perform
on that specific thread. This thread is create one time and kept
alive with a dummy port. The time taken inside the delegate is
0.008.
Conclusion
Concurrent or serial queue doesn't seems to make a big difference, however concurrent for me is not ok cause of the timestamps that I need to take to preserve the sequence of single picture. The fact that is surprising me is the frame drop using an ad-hoc thread, even if the time taken inside the delegate method is considerably less, the frame rate drops about 10fps. Just trusting in GCD the frame rate is better but the delegate methods takes more than 2 times to finish, this probably due to the fact that sometimes the AV queue is used by also the nsoperations. I can't really get why the time taken inside the delegate doesn't seems to be correlated to the fps. Shouldn't be the faster the better?
By further investigations it really seems that what is stealing time si in the process of adding and probably executing operation in queue.
I think you might be misunderstanding the meaning of isConcurrent. This is totally understandable, since it's incredibly poorly named. When you return YES from -isConcurrent what it really means is "I will handle any concurrency needs associated with this operation, and will otherwise behave asynchronously." In this situation, NSOperationQueue is free to call -start on your operation synchronously on the thread from which you add the operation. The NSOperationQueue is expecting that, because you've declared that you will manage your own concurrency, that your -start method will simply kick off an asynchronous process and return right away. I suspect this is the source of your problem.
If you implemented your operation by overriding -main then you almost certainly want to be returning NO from isConcurrent. To make matters more complicated, the behaviors associated with isConcurrent have changed over the years (but all that is covered in the official docs.) A fantastic explanation of how to properly implement a returns-YES-from-isConcurrent NSOperation can be found here.
The way I'm reading your question here, it doesn't sound like your NSOperation subclass actually needs to "manage its own concurrency", but rather that you simply want it to execute asynchronously, "on a background thread", potentially "concurrently" with other operations.
In terms of assuring the best performance for your AVCaptureVideoDataOutputSampleBufferDelegate callbacks, I would suggest making the queue you pass in to -setSampleBufferDelegate:queue: be (itself) concurrent and that it target the high-priority global concurrent queue, like this:
dispatch_queue_t queue = dispatch_queue_create("it.CloudInTouchLabs.avsession", DISPATCH_QUEUE_CONCURRENT);
dispatch_set_target_queue(queue, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0));
[dataOutput setSampleBufferDelegate:(id)self queue:queue];
Then you should make the delegate callback methods as lightweight as possible -- nothing more than packaging up the information you need to make the NSOperation and add it to the NSOperationQueue.
This should ensure that the callbacks always take precedence over the NSOperations. (It's my understanding that NSOperationQueue targets either the main queue (for the NSOperationQueue that's associated with the main thread and run loop) or the default priority background queue.) This should allow your callbacks to fully keep up with the frame rate.
Another important thing to take away here (that another commenter was getting at) is that if you're doing all your concurrency using GCD, then there is only one special thread -- the main thread. Other than that, threads are just a generic resource that GCD can (and will) use interchangeably with one another. The fact that a thread with a thread ID of X was used at one point to service your delegate callback, and at another point was used to do your processing is not, in and of itself, indicative of a problem.
Hope this helps!
I have an app where I'm downloading a number of resources from the network, and doing some processing on each one. I don't want this work happening on the main thread, but it's pretty lightweight and low-priority, so all of it can really happen on the same shared work thread. That seems like it'd be a good thing to do, because of the work required to set up & tear down all of these work threads (none of which will live very long, etc.).
Surprisingly, though, there doesn't seem to be a simple way to get all of this work happening on a single, shared thread, rather than spawning a new thread for each task. This is complicated by the large number of paths to achieving concurrency that seem to have cropped up over the years. (Explicit NSThreads, NSOperationQueue, GCD, etc.)
Am I over-estimating the overhead involved in spawning all of these threads? Should I just not sweat it, and use the easier thread-per-task approaches? Use GCD, and assume that it's smarter than I about thread (re)use?
Use GCD — it's the current official recommendation and it's less effort than any of the other solutions. If you explicitly need the things you pass in to occur serially (ie, as if on a single thread) then you can achieve that but it's probably smarter just to change, e.g.
[self doCostlyTask];
To:
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0), ^()
{
[self doCostlyTask];
dispatch_async(dispatch_get_main_queue(), ^()
{
// most UIKit tasks are permissible only from the main queue or thread,
// so if you want to update an UI as a result of the completed action,
// this is a safe way to proceed
[self costlyTaskIsFinished];
});
});
That essentially tells the OS "do this code with low priority wherever it would be most efficient to do it". The various things you post to any of the global queues may or may not execute on the same thread as each other and as the thread that dispatched them and may or may not occur concurrently. The OS applies the rules it considers optimal.
Exposition:
GCD is Apple's implementation of thread pooling, and they introduced closures (as 'blocks') at the same time to make it usable. So the ^(C-style args){code} syntax is a block/closure. That is, it's code plus the state of any variables (subject to caveats) that the code references. You can store and call blocks yourself with no GCD knowledge or use.
dispatch_async is a GCD function issues a block to the nominated queue. It executes the block on some thread at some time, and applies unspecified internal rules to do so in an optimal fashion. It'll judge that based on factors such as how many cores you have, how busy each is, what it's currently thinking on power saving (which may depend on power source), how the power costs for that specific CPU work out, etc.
So as far as the programmer is developed, blocks make code into something you can pass around as an argument. GCD lets you request that blocks are executed according to the best scheduling the OS can manage. Blocks are very lightweight to create and copy — a lot more so than e.g. NSOperations.
GCD goes beyond the basic asynchronous dispatch in the above example (eg, you can do a parallel for loop and wait for it to finish in a single call) but unless you have specific needs it's probably not all that relevant.
Surprisingly, though, there doesn't seem to be a simple way to get all
of this work happening on a single, shared thread, rather than
spawning a new thread for each task.
This is exactly what GCD is for. GCD maintains a pool of threads that can be used for executing arbitrary blocks of code, and it takes care of managing that pool for best results on whatever hardware is at hand. This avoids the cost of constantly creating and destroying threads and also saves you from having to figure out how many processors are available, etc.
Tommy provides the right answer if you really care that only a single thread should be used, but it sounds like you're really just trying to avoid creating one thread per task.
This is complicated by the large number of paths to achieving
concurrency that seem to have cropped up over the years. (Explicit
NSThreads, NSOperationQueue, GCD, etc.)
NSOperationQueue uses GCD, so you can use that if it makes life easier than using GCD directly.
Use GCD, and assume that it's smarter than I about thread (re)use?
Exactly.
I would use NSOperationQueue or GCD and profile. Can't imagine thread overhead will beat out network delays.
NSOperationQueue would let you limit the number of simultaneous operations, if they end up getting too greedy. In fact, you can limit it to one if you need to.