concurrency-kit
Last Update: 13/March/2019.
If you like the project, please give it a star β It will show the creator your appreciation and help others to discover the repo.
βοΈ About
iOS
development [Task
, Atomic
, Lock
, etc.].
π Installation
CocoaPods
concurrency-kit
is available via CocoaPods
pod 'concurrency-kit', '~> 1.0.0'
Manual
You can always use copy-paste
the sources method
π₯ Features
-
Atomics - synchronization primitive that is implemented in several forms:
Generic
,Int
andBool
.-
Fast
. Under the hood a mutex (pthread_mutex_lock
) that is more efficient thanOSSpinLock
and faster thanNSLock
. -
Throwable
. You can safely throwErrors
and be able to delegate the handling.
-
-
Locks - contains a number of locks, such as:
-
UnfairLock
- A lock which causes a thread trying to acquire it to simply wait in a loop ("spin") while repeatedly checking if the lock is available. -
ReadWriteLock
- AnRW
lock allows concurrent access for read-only operations, while write operations require exclusive access. -
Mutex
- Allows only one thread to be active in a given region of code.
-
-
DispatchQueue+Extensions - extended
DispatchQueue
, whereasyncAfter
andonce
methods add convenience. -
Task - A unit of work that performs a specific job and usually runs concurrently with other tasks.
- Tasks can be
grouped
- meaning that you are able to compose the tasks, similar toFutures & Promises
and execute them serially. - Tasks can be
sequenced
- meaning that you are able to compose differentgroups
and execute them concurrently. No need to repeatedly useDispatchGroup
(enter
/leave
).
- Tasks can be
- Thoroughly tested.
π Examples
Task
In order to create a Task
, you need to simply use the Task
struct and the trailing closure
syntax:
let uploadingTask = Task { controller in
uploader(photos) { result in
switch result {
case .success:
controller.finish()
case .failure(let error):
controller.fail(with error)
}
}
}
uploadingTask.perform { outcome in
handle(outcome)
}
You can group the tasks, so the concurrent operations will be performed sequentially, one after another. Then, you can chain a completion closure to handle the outcome:
let filesToUpload = [file, photo, video, xml]
let group = Task.group(fileToUpload)
group.perform { outcome in
handle(outcome)
}
Or you can concurrently perform a collection of tasks. They will be executed asynchronously, in parallel (if possible) or concurrently, that is up to the GCD
:
let filesToUpload = [file, photo, video, xml]
let group = Task.sequence(filesToUpload)
group.perform { outcome in
handle(outcome)
}
Atomics
Guarantees safe mutation of a property in multiple async dispatch queues. Simply wrap a property in Atomic
type:
let atomic = Atomic(0)
DispatchQueue.global().async {
atomic.modify { $0 + 1 }
}
DispatchQueue.global().async {
atomic.modify { $0 + 1 }
}
You can also use slightly more performance-friendly AtomicInt
and AtomicBool
classes, hence there is no dynamic dispatch involved (though Swift
compiler is smart enough to apply complier optimization called compile-time generic specialization
)
Locks
Read Write Lock
A syncronozation primitive that solves one of the readersβwriters problems:
let rwLock = ReadWriteLock()
rwLock.writeLock()
sharedValue += 1
rwLock.unlock()
Or you can restrict the reading access, so other threads will not be able to read the mutated value of a property until the lock will be released:
let rwLock = ReadWriteLock()
rwLock.readLock()
sharedValue += 1
rwLock.unlock()
Unfair Lock
A lock which causes a thread trying to acquire it to simply wait in a loop ("spin"), while repeatedly checking if the lock is available:
let unfairLock = UnfairLock()
unfairLock.lock()
sharedValue += 1
unfairLock.unlock()
Mutex
Used to protect shared resources. A mutex is owned by the task that takes it. In a given region of code only one thread is active:
let mutex = Mutex()
mutex.withCriticalScope {
return sharedValue += 1
}
Dispatch Queue
There is a convenience method that removes the need to pass .now() + time
in order to make an async call:
DispatchQueue.main.asyncAfter(seconds: 2.5) {
expectation.fulfill()
}
Also, DispatchQueue.once
was returned back::
// The following concurrentQueue is called multiple times, though the caughtValue will be set to value only once.
concurrentQueue.async {
DispatchQueue.once(token: "caught") {
caughtValue = value
}
}
π¨βπ» Author
π Licence
The project is available under MIT licence