Threading is one of the most difficult things when you do programming. Fortunately, in comparisons to another frameworks, the Apple’s SDK handles the problem exceptionally well. At least it is my impression.
All chunks of job are arranged into Blocks
or Functions
/Methods
. I’d like to mention that the Blocks
have ability to capture their surrounding state. They close variables around that are in scope at the time the block is declared that’s why we call them also Closures
[1]
Threads
= Cores
/ (1-Blocking Factor
). Here the switch of CPU context itself does not impact much into performance, because the thread probably is waiting for a signal anyway.What makes threading difficult is the data consistency. Imagine the situation when there are two threads that have a pointer to the same array which suppose to contains unique items. Both threads can modify and read the array. Let’s say both of the threads want to append the same item at exactly the same time to this array. What they do is to check if the item already exists in the array so they iterate through the array for checking the item if it is a duplicate and if not both of them will append it.
The array will contains two the same items or even works the app will crash because the array might be mutated at the time the other threat iterate through it.
The solution is to use mutex
for blocking threads in order to avoid override the data.
var mutex = pthread_mutex_t()
pthread_mutex_init(&mutex, nil)
pthread_mutex_lock(&mutex)
// do atomic job
pthread_mutex_unlock(&mutex)
pthread_mutex_destroy(&mutex)
Another mechanism for syncing thread is semaphore, but you need to be aware of potential deadlock. Below you can see one
let semaphore = DispatchSemaphore(value: 0)
semaphore.wait() // DEADLOCK
semaphore.signal()
The current thread stops and wait for a signal that can not be send in this case, because we try to send it from the same thread which is stopped.
The easiest way to start a new thread is to use a method that comes from base class NSObject
performSelector(inBackground: #selector(job), with: nil)
The libdispatch is a part of core library which takes care about threading.
In particular how to manage sync calls
dispatch_once
: Does not exists in SDK anymore [1] Here is an alternativeprivate lazy var foo: Void = {
// Do this once
}()
dispatch_sync
: locks the current thread until the passed block is executed on the separated threaddispatch_async
: starts executing the passed block on a separate thread, but the current one keeps running// without "attributes" we will get serial queue
let queue = DispatchQueue(label: "important.job", qos: .default, attributes: .concurrent)
queue.async {
// do stuff
}
DispatchQueue.main.async {
<#code#>
}
DispatchQueue.global(qos: .background).async {
<#code#>
}
NSOperationQueue
does use GCD on iOS 4.0 and later”
it says also quote:
let thread = Thread(target: self, selector: #selector(job), object: nil)
// or
let thread = Thread {
<#code#>
}
// and then start the tread
thread.start()
class MyVeryExpensiveOperation: Operation {
override func main() {
if self.isCancelled { return }
// Some chunk of time consuming task
if self.isCancelled { return }
// Some another chunk of time consuming task
// and so on...
}
}
let queue = OperationQueue()
queue.name = "Queue Name"
queue.maxConcurrentOperationCount = 1
let myOperation = MyVeryExpensiveOperation()
queue.addOperation(myOperation)
queue.addOperation {
// some another job passed by block
}
There is no good idea to handle threads from low level because it affect highly into development time of an application, whoever as all of us we are curious about everything, so I am going to show you how to do pthreading
on iOS. Here is an example:
var user_interactive_thread: pthread_t?
var user_interactive_qos_attr = pthread_attr_t()
return_value = pthread_attr_init(&user_interactive_qos_attr)
return_value = pthread_attr_set_qos_class_np(&user_interactive_qos_attr, QOS_CLASS_USER_INTERACTIVE, 0)
return_value = pthread_create(&user_interactive_thread, &user_interactive_qos_attr, { (x:UnsafeMutableRawPointer) in
print("New pthread job")
return nil
}, nil)