Is it possible to specify the `DispatchQueue` for `DispatchQueue.concurrentPerform`?
The author’s attempt to specify the queue quality of service (QoS) is incorrect. The concurrentPerform
uses the current queue’s QoS if it can. You can confirm this by tracking through the source code:
concurrentPerform
calls_swift_dispatch_apply_current
._swift_dispatch_apply_current
callsdispatch_apply
with0
, i.e.,DISPATCH_APPLY_AUTO
, which is defined as a ...... Constant to pass to
dispatch_apply()
ordispatch_apply_f()
to request that the system automatically use worker threads that match the configuration of the current thread as closely as possible.When submitting a block for parallel invocation, passing this constant as the queue argument will automatically use the global concurrent queue that matches the Quality of Service of the caller most closely.
This can also be confirmed by following
dispatch_apply
calldispatch_apply_f
in which usingDISPATCH_APPLY_AUTO
results in the call to_dispatch_apply_root_queue
. If you keep tumbling down the rabbit hole of swift-corelibs-libdispatch, you’ll see that this actually does use a global queue which is the same QoS as your current thread.
Bottom line, the correct way to specify the QoS is to dispatch the call to concurrentPerform
to the desired queue, e.g.:
DispatchQueue.global(qos: .userInitiated).async {
DispatchQueue.concurrentPerform(iterations: 3) { (i) in
...
}
}
This is easily verified empirically by adding a break point and looking at the queue in the Xcode debugger:
Needless to say, the suggestion of adding the let _ = ...
is incorrect. Consider the following:
DispatchQueue.global(qos: .utility).async {
let _ = DispatchQueue.global(qos: .userInitiated)
DispatchQueue.concurrentPerform(iterations: 3) { (i) in
...
}
}
This will run with “utility” QoS, not “user initiated”.
Again, this is easily verified empirically:
See WWDC 2017 video Modernizing Grand Central Dispatch for a discussion about DISPATCH_APPLY_AUTO
and concurrentPerform
.
How does a DispatchQueue work? (specifically multithreading)
Why ask this?
This is a fairly broad question about the internals of grand-central-dispatch
. I had difficulty understanding the dump
ed output because the original WWDC '10 videos and slides for GCD are no longer public. I also didn't know about the open-source libdispatch
repo (thanks Rob). That needn't be a problem, but there are no related QAs on SO explaining the topic in detail.
Why GCD?
According to the WWDC '10 GCD transcripts (Thanks Rob), the main idea behind the API was to simplify the boilerplate associated with using the #selector
API for multithreading.
Benefits of GCD
Apple released a new block
-based API instead of going with function pointers, to also enable type-safe code that wouldn't crash if the block had the wrong type signature. Using typedef
s also made code cleaner when used in function parameters, local variables and @property
declarations. Queues allow you to capture code and some state as a chunk of data that get managed, enqueued and executed automatically behind the scenes.
The same session mentions how GCD manages low-level threads under the hood. It enqueues blocks to execute on threads when they need to be executed and then releases those threads (PThread
s to be precise) when they are no longer referenced. GCD manages threads automatically and doesn't expose this API - when a DispatchWorkItem
is dequeued GCD creates a thread for this block to execute on.
Drawbacks of performSelector
performSelector:onThread:withObject:waitUntilDone:
has numerous drawbacks that suggest poor design for the modern challenges of concurrency, waiting, synchronisation. leads to pyramids of doom when switching threads in a func
. Furthermore, the NSObject.performSelector
family of threading methods are inflexible and limited:
- No options to optimise for concurrent, initially inactive, or synchronisation on a particular thread. Unlike GCD.
- Only selectors can be dispatched on to new threads (awful).
- Lots of threads for a given function leads to messy code (pyramids of doom).
- No support for queueing without a limited (at the time when GCD was announced in iOS 4)
NSOperation
API.NSOperation
s are a high-level, verbose API that became more powerful after incorporating elements of dispatch (low-level API that became GCD) in iOS 4. - Lots of bugs related to unhandled invalid
Selector
errors (type safety).
DispatchQueue
internals
I believe the xref
, ref
and sref
are internal registers that manage reference counts for automatic reference counting. GCD calls dispatch_retain
and dispatch_release
in most cases when needed, so we don't need to worry about releasing a queue after all its blocks have been executed. However, there were cases when a developer could call retain
and release
manually when trying to ensure the queue is retained even when not directly in use. These registers allow libDispatch
to crash when release
is called on a queue with a positive reference count, for better error handling.
When calling a block with DispatchQueue.global().async
or similar, I believe this increments the reference count of that queue (xref
and ref
).
The variables in the question are not documented explicitly, but from what I can tell:
xref
counts the number of external references to a generalDispatchQueue
.ref
counts the total number of references to a generalDispatchQueue
.sref
counts the number of references to API serial/concurrent/runloop queues, sources and mach channels (these need to be tracked differently as they are represented using different types).
in-barrier
looks like an internal state flag (DispatchWorkItemFlag
) to track whether new work items submitted to a concurrent queue should be scheduled or not. Only once the barrier work item finishes, the queue returns to scheduling work items that were submitted after the barrier. in-flight
means that there is no barrier in force currently.
state
is also not documented explicitly but I presume points to memory where the block can access variables from the scope where the block was scheduled.
How to create dispatch queue in Swift 3
Creating a concurrent queue
let concurrentQueue = DispatchQueue(label: "queuename", attributes: .concurrent)
concurrentQueue.sync {
}
Create a serial queue
let serialQueue = DispatchQueue(label: "queuename")
serialQueue.sync {
}
Get main queue asynchronously
DispatchQueue.main.async {
}
Get main queue synchronously
DispatchQueue.main.sync {
}
To get one of the background thread
DispatchQueue.global(qos: .background).async {
}
Xcode 8.2 beta 2:
To get one of the background thread
DispatchQueue.global(qos: .default).async {
}
DispatchQueue.global().async {
// qos' default value is ´DispatchQoS.QoSClass.default`
}
If you want to learn about using these queues .See this answer
How to dispatch a block with parameter on main queue or thread
Nope. Wrapping blocks is exactly what you have to do in this case. In code:
void (^block)(id someArg) = someBlock;
id object = someObject;
dispatch_async(dispatch_get_main_queue(), ^{
block(someObject);
});
It may look a little strange at first, but this style makes the dispatch APIs so much simpler and the automatic retaining of captured variables makes it possible. I'm a little surprised you ran into problems. What were they?
GCD dispatch_set_target_queue function's 1st parameter type
You can think of dispatch_object_t
as the "base class" of all the dispatch object types.
In "plain" C this uses the transparent union GCC extension, which essentially allows all pointer types in the union to be treated interchangeably with the union type when used as a function argument.
the macro below the block you quoted from dispatch/object.h explains the connection with dispatch_queue_t
:
#define DISPATCH_DECL(name) typedef struct name##_s *name##_t
and then later on in dispatch/queue.h
DISPATCH_DECL(dispatch_queue);
i.e. dispatch_queue_t
matches the _dq
member of the transparent union and hence is a valid type to pass to the dispatch_object_t
argument of dispatch_set_target_queue
.
FWIW in Objective-C and C++ the dispatch_object_t
superclass relationship is expressed using the respective object type system, c.f. the other sections in the dispatch_object_t
area of dispatch/object.h.
Related Topics
Change Tabview Indicator Swiftui
Use of Undeclared Type 'Viewcontroller' When Unit Testing My Own Viewcontroller in Swift
How to Code Initwithcoder in Swift
How to Repeat Animation (Using Uiviewpropertyanimator) Certain Number of Times
Swiftui Swizzling Disabled by Default, Phone Auth Not Working
What Does <> (Angle Brackets) Do on Class Names in Swift
How to Setup a Second Component with a Uipickerview
The File "Xxx.Mp4" Couldn't Be Opened Because You Don't Have Permission to View It
Swift Didset Get Index of Array
Generate an Integer Binary Representation Using Swift
Count Number of Decimal Places in a Float (Or Decimal) in Swift
Binary Operator '===' Cannot Be Applied to Operands of Type 'Any' and 'Uibarbuttonitem!'
How to Add a Watermark to an Image Using This Code
Subclass Nsapplication in Swift
How to Resolve This Build Issue - Cannot Assign to Property: 'Date' Is a Get Only Property
Lazy Readonly Property in Swift
Getting Path for Resource in Command Line Tool
My Data Changes in Uitableviewcell When I Scroll Down and Get Back