Göktuğ Gümüş
Sr. iOS Developer @bundle
Swift ile
Concurrent Programming
A4 A5 A6 A7 A8 A9 A10
Concurrency
• Bağımsız olarak yürütülen görevlerin
kompozisyonu
Parallelism
• Yakın ilişkili işlemlerin aynı anda yürütülmesi
CPU
• Verileri işleyen ve yazılım komutlarını
gerçekleştiren donanım
• Her bir çekirdeğin bir clock-cycle’ında aynı
anda sadece bir işlem gerçekleştirebilir.
Thread (İş Parçacığı)
• “Bir işin eş zamanlı olarak işlenen her bir
bölümüdür.” - Wikipedia
• Uygulama çalıştığında işletim sistemi belli
sayıda thread ayırır (Thread pool)
• Main thread ve GCD threads
Concurrency
Concurrency
• Tek çekirdekli CPU’lu cihazlarda birden fazla işlemin eş
zamanlı olarak gerçekleştirilmesi
• Bir çekirdekte aynı anda bir işlem?
• İşlemler aynı anda gerçekleşiyormuş izlenimi verir.
• Bir işlem bitmeden, diğerini başlatır. (time-slicing)
• İşlemler arasında önceliklerine göre geçiş yapar.
(context switch)
Database
Networking
User Interface
Touch!
Parallelism
Parallelism
• Çok çekirdekli CPU’lu cihazlarda birden fazla
işlemin aynı anda gerçekleştirilmesi
• Bir işlem, alt parçalara ayrılıp her bir
çekirdekte aynı anda işlenebilir.
• Parallelism, çok çekirdekli CPU donanımına
ihtiyaç duyar.
• 5 kişinin 1 yatağı aynı anda yapması
• Her bir kişinin kendi yataklarını yapması
Hangisini yapılandırmak daha komplekstir?
Concurrency
• Birden fazla şeyle aynı anda başa çıkılması
• Yapılandırmaya odaklanır.
Parallelism
• Birden fazla şeyin aynı anda yapılması
• Execution ile alakalıdır.
General Central Dispatch
(GCD)
GCD
• Closure’ları işlemeye izin veren Queue-Based
API
• iOS, MacOS, WatchOS ve TvOS’ta
kullanabilir.
• Thread’lerin üstüne inşa edilmiş soyut
yapılar; Dispatch Queue ve Run Loop
Dispatch Queue
Dispatch Queue
Dispatch Queue
Dispatch Queue () -> ()
Dispatch Queue
Worker Dispatch Queue () -> ()
Dispatch Queue
Dispatch QueueDispatch Queue () -> ()Worker
Dispatch Queue
Dispatch QueueDispatch QueueWorker
Dispatch Queue
Dispatch QueueDispatch Queue
Sync ve Async Execution
Sync ve Async Execution
• Sync: Bir işlem bitene kadar, diğer işlemler
bekler.
• Async: Bir işlem execute edildiğinde sonucu
hemen döner ve işlemin bitmesini
beklemeden diğeri başlar.
Asynchronous Execution
Dispatch Queue
Asynchronous Execution
Dispatch Queue () -> ()
Asynchronous Execution
Dispatch Queue () -> () () -> ()
Asynchronous Execution
Dispatch Queue () -> () () -> () () -> ()
Asynchronous Execution
Dispatch Queue () -> () () -> () () -> ()Worker
Asynchronous Execution
Dispatch Queue () -> () () -> ()Worker
Asynchronous Execution
Dispatch Queue () -> ()Worker
Asynchronous Execution
Dispatch QueueWorker
Asynchronous Execution
Dispatch Queue
Synchronous Execution
Worker
Thread
() -> ()
() -> ()() -> ()
Dispatch Queue
Synchronous Execution
Worker
Thread
() -> ()
() -> ()
() -> ()Dispatch Queue
Synchronous Execution
Worker
Thread
() -> ()
() -> ()
() -> () () -> ()Dispatch Queue
Synchronous Execution
Dispatch QueueWorker
Thread () -> ()() -> ()
() -> () () -> ()
Synchronous Execution
Worker
Thread () -> ()
() -> () () -> ()Dispatch Queue
Synchronous Execution
Worker
Thread () -> ()
() -> () () -> ()
Dispatch Queue
Synchronous Execution
Worker
Thread
() -> ()
Dispatch Queue
Synchronous Execution
Worker
Thread
() -> ()Dispatch Queue
Synchronous Execution
Worker
Thread
() -> ()Dispatch Queue
Synchronous Execution
Worker
Thread
Dispatch Queue
Synchronous Execution
Worker
Thread
Dispatch Queue
Serial ve Concurrent Queue
• Dispatch Queue, serial ya da concurrent
olabilir.
Serial Queue
• Aynı anda sadece bir işlem gerçekleşir
• İşlemler FIFO sırasına göre işler.
Serial Queue
let serialQueue = DispatchQueue(label: "customSerialQueue")
for i in 0...3 {
serialQueue.sync {
sleep(UInt32(3-i))
print(i)
}
}
print("NSIstanbul")
Serial Queue
let serialQueue = DispatchQueue(label: "customSerialQueue")
for i in 0...3 {
serialQueue.sync {
sleep(UInt32(3-i))
print(i)
}
}
print("NSIstanbul")
// Prints
0
1
2
3
NSIstanbul
Serial Queue
let serialQueue = DispatchQueue(label: "customSerialQueue")
for i in 0...3 {
serialQueue.async {
sleep(UInt32(3-i))
print(i)
}
}
print("NSIstanbul")
Serial Queue
let serialQueue = DispatchQueue(label: "customSerialQueue")
for i in 0...3 {
serialQueue.async {
sleep(UInt32(3-i))
print(i)
}
}
print("NSIstanbul")
// Prints
NSIstanbul
0
1
2
3
Concurrent Queue
• İşlemlere FIFO sırasına göre başlanır fakat ne zaman
biteceği belli değildir.
• Bir işleme başlamak için diğer işlemin bitmesini beklemez.
Concurrent Queue
let concurrentQueue = DispatchQueue(label:“customConcurrentQueue”,
attributes: .concurrent)
for i in 0...3 {
concurrentQueue.async {
sleep(UInt32(3-i))
print(i)
}
}
print("NSIstanbul")
Concurrent Queue
let concurrentQueue = DispatchQueue(label:“customConcurrentQueue”,
attributes: .concurrent)
for i in 0...3 {
concurrentQueue.async {
sleep(UInt32(3-i))
print(i)
}
}
print("NSIstanbul")
// Prints
NSIstanbul
3
2
1
0
Concurrent Queue
let concurrentQueue = DispatchQueue(label:“customConcurrentQueue”,
attributes: .concurrent)
for i in 0...3 {
concurrentQueue.sync {
sleep(UInt32(3-i))
print(i)
}
}
print("NSIstanbul")
Concurrent Queue
let concurrentQueue = DispatchQueue(label:“customConcurrentQueue”,
attributes: .concurrent)
for i in 0...3 {
concurrentQueue.sync {
sleep(UInt32(3-i))
print(i)
}
}
print("NSIstanbul")
// Prints
0
1
2
3
NSIstanbul
Main Queue
ve
Global Queue’lar
Main Queue
• Uygulama çalıştığında otomatik olarak
oluşan özel bir queue
• İşlemleri ard arda (serially) gerçekleştirir.
• İşlemleri her zaman Main thread’te çalıştırır.
• UI işlemleri her zaman Main thread’te
yapılmalıdır.
Main Queue
for i in 0...3 {
DispatchQueue.main.async {
sleep(UInt32(3-i))
print(i)
}
}
print("NSIstanbul")
Main Queue
for i in 0...3 {
DispatchQueue.main.async {
sleep(UInt32(3-i))
print(i)
}
}
print("NSIstanbul")
// Prints
NSIstanbul
0
1
2
3
Main Queue
for i in 0...3 {
DispatchQueue.main.sync {
sleep(UInt32(3-i))
print(i)
}
}
print("NSIstanbul")
Main Queue
for i in 0...3 {
DispatchQueue.main.sync {
sleep(UInt32(3-i))
print(i)
}
}
print("NSIstanbul")
// Runtime Error
error: Execution was interrupted, reason: EXC_BAD_INSTRUCTION
(code=EXC_I386_INVOP, subcode=0x0).
1. Sync queue Main Queue’yu bloklar.
2. Kod bloğu işlemini bitirene kadar, Main
Queue’nun thread’ini bloklar.
3. Kod bloğu çalışmayı sonsuza kadar bekler
çünkü çalışması gereken thread bloklanmıştır.
DispatchQueue.main.sync {}
dispatch_sync; kod bloklarını çalıştırmaz
sadece sıraya alır. Çalıştırma işlemi
gelecek zamanda gerçekleşir.
Global Queue’lar
• İşletim sistemi bize bir takım concurrent global
queue’lar sağlar.
• Genellikle arka planda gerçekleştirmek istediğimiz,
UI ile alakalı olmayan işler için kullanırız.
• Yapacağınız işlemin önemine, yüküne ve ne kadar
uzun süreceğine göre global queue seçimi yapılır.
• Quality of Service (QoS)
Quality of Service Classes
UI
User Interactive
IN
User Initiated
UT
Utility
BG
Background
Quality of Service Classes
• CPU görev önceliklendirmesi
• CPU etkin kullanımı
• I/O önceliği
• Her cihaza/platforma özgün konfigürasyonlar
Quality of Service Classes
User Interactive
User Initiated
Utility
Background
Main thread, animasyonlar
Hızlı sonuçlar
Uzun süren işler
Kullanıcı tarafında önemsiz işler
Quality of Service Classes
User Interactive
User Initiated
Utility
Background
UI’yı güncellerken aktif bir şekilde kullanılacak mı?
Hızlı sonuçlar
Uzun süren işler
Kullanıcı tarafında önemsiz işler
Quality of Service Classes
User Interactive
User Initiated
Utility
Background
UI’yı güncellerken aktif bir şekilde kullanılacak mı?
Kullanıcının etkileşime devam etmesi için gerekli mi?
Uzun süren işler
Kullanıcı tarafında önemsiz işler
Quality of Service Classes
User Interactive
User Initiated
Utility
Background
UI’yı güncellerken aktif bir şekilde kullanılacak mı?
Kullanıcının etkileşime devam etmesi için gerekli mi?
Kullanıcı bu işlemin gerçekleştiğinin farkında mı?
Kullanıcı tarafında önemsiz işler
Quality of Service Classes
User Interactive
User Initiated
Utility
Background
UI’yı güncellerken aktif bir şekilde kullanılacak mı?
Kullanıcının etkileşime devam etmesi için gerekli mi?
Kullanıcı bu işlemin gerçekleştiğinin farkında mı?
Kullanıcı işlemin farkında değil mi?
Quality of Service Classes
UI IN UT BG
Quality of Service Classes
UI IN UT BG
Quality of Service Classes
Main Thread GCD Thread
Quality of Service Classes
Main Thread Main ThreadGCD Thread
Quality of Service Classes
Main Thread Main ThreadGCD Thread
Quality of Service Classes
Main Thread Main ThreadGCD Thread
Quality of Service Classes
Main Thread Main ThreadGCD Thread
Uygulamanızı Yapılandırma
Uygulamanızı Yapılandırma
Database
Dispatch Queue
Data Transform
Dispatch Queue
Networking
Dispatch Queue
User Interface
Main Queue
Chaining
let queue = DispatchQueue(label: "customQueue")
queue.async {
let smallImage = image.resize(to: rect)
}
DispatchQueue.main.async {
imageView.image = smallImage
}
Chaining vs Grouping
Chaining Grouping
Chaining vs Grouping
Chaining Grouping
Chaining vs Grouping
Chaining Grouping
Chaining vs Grouping
Chaining Grouping
Grouping
Database
Dispatch Queue
Data Transform
Dispatch Queue
Networking
Dispatch Queue
User Interface
Main Queue
Grouping
Database
Dispatch Queue
Data Transform
Dispatch Queue
Networking
Dispatch Queue
User Interface
Main Queue
Grouping
Database
Dispatch Queue
Data Transform
Dispatch Queue
Networking
Dispatch Queue
User Interface
Main Queue
Dispatch Group
let group = DispatchGroup()
Grouping
Database
Dispatch Queue
Data Transform
Dispatch Queue
Networking
Dispatch Queue
User Interface
Main Queue
Dispatch Group
let group = DispatchGroup()
queue.async(group: group).async {...}
1
Grouping
Database
Dispatch Queue
Data Transform
Dispatch Queue
Networking
Dispatch Queue
User Interface
Main Queue
Dispatch Group
let group = DispatchGroup()
queue.async(group: group).async {...}
2
queue2.async(group: group).async {...}
Grouping
Database
Dispatch Queue
Data Transform
Dispatch Queue
Networking
Dispatch Queue
User Interface
Main Queue
Dispatch Group
let group = DispatchGroup()
queue.async(group: group).async {...}
3
queue2.async(group: group).async {...}
queue3.async(group: group).async {...}
Grouping
Database
Dispatch Queue
Data Transform
Dispatch Queue
Networking
Dispatch Queue
User Interface
Main Queue
Dispatch Group
let group = DispatchGroup()
queue.async(group: group).async {...}
3
queue2.async(group: group).async {...}
queue3.async(group: group).async {...}
group.notify(queue: .main) {...}
Grouping
Database
Dispatch Queue
Data Transform
Dispatch Queue
Networking
Dispatch Queue
User Interface
Main Queue
Dispatch Group
let group = DispatchGroup()
2
queue2.async(group: group).async {...}
queue3.async(group: group).async {...}
group.notify(queue: .main) {...}
Grouping
Database
Dispatch Queue
Data Transform
Dispatch Queue
Networking
Dispatch Queue
User Interface
Main Queue
Dispatch Group
let group = DispatchGroup()
1
queue3.async(group: group).async {...}
group.notify(queue: .main) {...}
Grouping
Database
Dispatch Queue
Data Transform
Dispatch Queue
Networking
Dispatch Queue
User Interface
Main Queue
Dispatch Group
let group = DispatchGroup()
group.notify(queue: .main) {...}
Grouping
Database
Dispatch Queue
Data Transform
Dispatch Queue
Networking
Dispatch Queue
User Interface
Main Queue
Dispatch Group
let group = DispatchGroup()
let group = DispatchGroup()
let networkingQueue = DispatchQueue(label: "networkingQueue",
qos: .utility, attributes: .concurrent)
let databaseQueue = DispatchQueue(label: "databaseQueue",
qos: .background, attributes: .concurrent)
networkingQueue.async(group: group) {
print("API çağrıları")
}
databaseQueue.async(group: group) {
print("Database işlemleri")
}
group.notify(queue: DispatchQueue.main) {
print("Queuelardaki işlemler bitti")
}
let group = DispatchGroup()
let networkingQueue = DispatchQueue(label: "networkingQueue",
qos: .utility, attributes: .concurrent)
let databaseQueue = DispatchQueue(label: "databaseQueue",
qos: .background, attributes: .concurrent)
group.enter()
networkingQueue.async {
print("API çağrıları")
group.leave()
}
group.enter()
databaseQueue.async(group: group) {
print("Database işlemleri")
group.leave()
}
group.notify(queue: DispatchQueue.main) {
print("Queuelardaki işlemler bitti")
}
Teşekkürler
Sorular?

Concurrent Programming with Swift