rambler.ios #4: Задачи синхронизации. Классические и...
Post on 14-Apr-2017
1.159 Views
Preview:
TRANSCRIPT
Егор Толстой Ведущий iOS разработчик @ Rambler&Co
Задачи синхронизацииКлассические и прикладные решения
Синхронизация
•Что такое синхронизация
•Решение проблем синхронизации
•Классические задачи синхронизации
Синхронизация
•Синхронизация - обеспечение правильной совместной работы нескольких потоков.
Синхронизация
Алиса
Боб
Поток 1 Поток 2
Синхронизация
Алиса Боб
Поток 1 Поток 2
Синхронизация
Алиса Боб
Поток 1 Поток 2
Синхронизация
Алиса
Боб
Поток 1 Поток 2
Синхронизация
•Состояние гонки - работа приложения зависит от последовательности вызова секции его кода разными потоками.
Синхронизация
func incrementSharedVariable { // counter == 0 counter += 1 }
Синхронизация
func incrementSharedVariable { // counter == 0 counter += 1 }
Поток 1 Поток 2 Поток 3
Синхронизация
func incrementSharedVariable { // counter == 0 counter += 1 }
Поток 1 Поток 2 Поток 3
counter ∈ [1,3]
Синхронизация
•Критическая секция - участок кода, в котором одновременно может находиться только один поток.
Синхронизация
func incrementSharedVariable { lockSection()
counter += 1 unlockSection()
}
Синхронизация
Поток 1 Поток 2 Поток 3
func incrementSharedVariable { lockSection()
counter += 1 unlockSection()
}
Синхронизация
Поток 1 Поток 2 Поток 3
func incrementSharedVariable { lockSection()
counter += 1 unlockSection()
}
Синхронизация
Поток 1 Поток 2 Поток 3
func incrementSharedVariable { lockSection()
counter += 1 unlockSection()
}
Синхронизация
Поток 1 Поток 2 Поток 3
func incrementSharedVariable { lockSection()
counter += 1 unlockSection()
}
counter == 3
Синхронизация
• Deadlock - несколько потоков блокируют выполнение друг друга.
Синхронизация
•У каждой задачи синхронизации есть определенные условия.
Синхронизация
Алиса Боб
Поток 1 Поток 2
Синхронизация
Алиса
Боб
Поток 1 Поток 2
Синхронизация
Алиса
Боб
Поток 1 Поток 2
Синхронизация
Алиса Боб
Поток 1 Поток 2
Синхронизация
•Передача сообщений между потоками - простое решение большинства проблем.
Синхронизация
Алиса Боб
а1: Съесть завтрак
а2: Поработать
а3: Съесть обед
а4: Позвонить Бобу
б1: Съесть завтрак
б2: Дождаться звонка Алисы
б3: Съесть обед
а1 < a2 < a3 < a4 б1 < б2 < б3
Синхронизация
Алиса Боб
а1: Съесть завтрак
а2: Поработать
а3: Съесть обед
а4: Позвонить Бобу
б1: Съесть завтрак
б2: Дождаться звонка Алисы
б3: Съесть обед
а1 < a2 < a3 < a4 б1 < б2 < б3
Синхронизация
Алиса Боб
а1: Съесть завтрак
а2: Поработать
а3: Съесть обед
а4: Позвонить Бобу
б1: Съесть завтрак
б2: Дождаться звонка Алисы
б3: Съесть обед
а1 < a2 < a3 < a4 б1 < б2 < б3
б3 > б2 > a4 > a3
Синхронизация
•События считаются параллельными, если, изучив код программы, мы не можем утверждать, какое из них произойдет первым.
Синхронизация
0
Алиса
Семафор
Боб
Синхронизация
0
Алиса
Семафор
Боб
Синхронизация
wait0
Алиса
Семафор
Боб
Синхронизация
wait-1
Алиса
Семафор
Боб
Синхронизация
wait-1Алиса
Семафор
Боб
Синхронизация
signal wait-1Алиса
Семафор
Боб
Синхронизация
signal wait0Алиса
Семафор
Боб
Синхронизация
signal wait0
Алиса
Семафор
Боб
Синхронизация
•Семафор создается с начальным значением,
• wait - уменьшает счетчик,
• signal - увеличивает счетчик.
Синхронизация
•Задача "Читатели-Писатели" Readers-Writers problem
Синхронизация
•Любое количество читателей в критической секции
•Единственный писатель в критической секции
Синхронизация
1.
Семафор "комната пуста"
off
Выключатель "читатели"
Читатели Писатели
0
Синхронизация
1.
Семафор "комната пуста"
off
Выключатель "читатели"
Читатели Писатели
0
Синхронизация
1.
Семафор "комната пуста"
off
Выключатель "читатели"
Читатели Писатели
0
Синхронизация
1.
Семафор "комната пуста"
off
Выключатель "читатели"
Читатели Писатели
0
Синхронизация
1.
Семафор "комната пуста"
off
Выключатель "читатели"
Читатели Писатели
0
Синхронизация
0.
Семафор "комната пуста"
on
Выключатель "читатели"
Читатели Писатели
1
Синхронизация
Семафор "комната пуста"
on
Выключатель "читатели"
Читатели Писатели
2
0.
Синхронизация
Семафор "комната пуста"
on
Выключатель "читатели"
Читатели Писатели
2
-1.
Синхронизация
Семафор "комната пуста"
on
Выключатель "читатели"
Читатели Писатели
2
-1.
Синхронизация
Семафор "комната пуста"
on
Выключатель "читатели"
Читатели Писатели
2
-1.
Синхронизация
Семафор "комната пуста"
on
Выключатель "читатели"
Читатели Писатели
2
-1.
Синхронизация
Семафор "комната пуста"
on
Выключатель "читатели"
Читатели Писатели
1
-1.
Синхронизация
Семафор "комната пуста"
off
Выключатель "читатели"
Читатели Писатели
0
0.
Синхронизация
Семафор "комната пуста"
off
Выключатель "читатели"
Читатели Писатели
0
0.
Синхронизация
Семафор "комната пуста"
off
Выключатель "читатели"
Читатели Писатели
0
1.
Синхронизация
• Голод - состояние, в котором поток может бесконечно ждать своей очереди. А может и дождаться.
Синхронизация
•Решение с использованием NSOperation
Синхронизация
class OperationScheduler { var readerQueue = NSOperationQueue() var writerQueue = NSOperationQueue() init () { // Читатели должны выполняться параллельно readerQueue.maxConcurrentOperationCount = MAX // Писатели должны выполняться последовательно writerQueue.maxConcurrentOperationCount = 1 }
... }
Синхронизация
func addReaderOperation(operation: NSBlockOperation) { // Зависим от выполнения всех писателей for writerOperation in writerQueue.operations { operation.addDependency(writerOperation) } readerQueue.addOperation(operation) }
Синхронизация
func addWriterOperation(operation: NSBlockOperation) { // Все читатели зависят от нового писателя for readerOperation in readerQueue.operations { readerOperation.addDependency(operation) } writerQueue.addOperation(operation) }
Синхронизация
func readersWriters() { let scheduler = OperationScheduler() scheduler.addWriter() for i in 1...5 { scheduler.addReader(i) } scheduler.addWriter() }
> writer fired > writer fired > reader 2 fired > reader 3 fired > reader 1 fired > reader 5 fired > reader 4 fired
Синхронизация
•Задача "Обедающие философы" Dining philosophers problem
Синхронизация
while(true) { think() getForks() eat() putForks() }
Синхронизация
•Не больше одного философа на вилку
•Не допустить deadlock
•Не допустить голод
Синхронизация
Ф.5
Ф.1
Ф.2
Ф.3
Ф.4
вилка1
вилка
1
вилка 1 вилка
1
вилка1
Синхронизация
Ф.5
Ф.1
Ф.2
Ф.3
Ф.4
вилка0
вилка
0
вилка 0 вилка
0
вилка0
Синхронизация
Ф.5
Ф.1
Ф.2
Ф.3
Ф.4
вилка0
вилка
0
вилка 0 вилка
0
вилка0
Синхронизация
Ф.5
Ф.1
Ф.2
Ф.3
Ф.4
вилка0
вилка
0
вилка 0 вилка
0
вилка0
Синхронизация
Ф.5
Ф.1
Ф.2
Ф.3
Ф.4
вилка0
вилка
0
вилка 0 вилка
0
вилка0
deadlock :(
Синхронизация
Ф.5
Ф.1
Ф.2
Ф.3
Ф.4
Семафор "официант"
4
вилка1
вилка
1
вилка 1 вилка
1
вилка1
Синхронизация
Ф.5
Ф.1
Ф.2
Ф.3
Ф.4
Семафор "официант"
3
вилка1
вилка
1
вилка 1 вилка
1
вилка1
Синхронизация
Ф.5
Ф.1
Ф.2
Ф.3
Ф.4
Семафор "официант"
3
вилка0
вилка
1
вилка 0 вилка
1
вилка1
Синхронизация
Ф.5
Ф.3
Ф.4
вилка0
вилка
вилка 0 вилка
1
вилка1
Ф.2
Семафор "официант"
2
Ф.1
1
Синхронизация
Ф.5
Ф.3
Ф.4
вилка0
вилка
0
вилка 0 вилка
1
вилка1
Ф.2
Семафор "официант"
2
Ф.1
Синхронизация
Ф.5
Ф.3
Ф.4
вилка0
вилка
0
вилка 0 вилка
1
вилка1
Ф.2
Семафор "официант"
1
Ф.1
Синхронизация
Ф.5
Ф.3
Ф.4
вилка0
вилка
0
вилка 0 вилка
0
вилка1
Ф.2
Семафор "официант"
1
Ф.1
Синхронизация
Ф.5
Ф.3
Ф.4
вилка0
вилка
0
вилка 0 вилка
0
вилка1
Ф.2
Семафор "официант"
0
Ф.1
Синхронизация
Ф.5
Ф.3
Ф.4
вилка0
вилка
0
вилка 0 вилка
0
вилка0
Ф.2
Семафор "официант"
0
Ф.1
Синхронизация
Ф.5
Ф.3
Ф.4
вилка0
вилка
0
вилка 1 вилка
0
вилка0
Ф.2
Семафор "официант"
1
Ф.1
Синхронизация
Ф.5
Ф.3
Ф.4
вилка0
вилка
0
вилка 0 вилка
0
вилка0
Ф.2
Семафор "официант"
0
Ф.1
Синхронизация
Ф.5
Ф.3
Ф.4
вилка1
вилка
0
вилка 0 вилка
0
вилка0
Ф.2
Семафор "официант"
1
Ф.1
Синхронизация
Ф.5
Ф.3
Ф.4
вилка0
вилка
0
вилка 0 вилка
0
вилка0
Ф.2
Семафор "официант"
0
Ф.1
Синхронизация
•Решение с использованием NSOperation
Синхронизация
class DiningPhilosophersOperationScheduler { var philosopherQueues = [NSOperationQueue]() init() { for _ in 0...4 { let philosopherQueue = NSOperationQueue() philosopherQueue.maxConcurrentOperationCount = 1 philosopherQueues.append(philosopherQueue) } }
... }
Синхронизация
class PhilosopherBlockOperation: NSBlockOperation { init(index: Int) { super.init()
addExecutionBlock({ print("philosopher \(index) is eating") sleep(2) print("philosopher \(index) puts forks") }) } }
Синхронизация
func startPhilosopher(index: Int) { let operation = PhilosopherBlockOperation(index: index) let leftQueue = getLeftPhilosopher(index) let leftPhilosopherEats = leftQueue.operationCount > 0 if (leftPhilosopherEats) { for leftOperation in leftQueue.operations { operation.addDependency(leftOperation) } }
// То же и для правого соседа
let currentPhilosopherQueue = philosopherQueues[index] currentPhilosopherQueue.addOperation(operation) }
Синхронизация
func diningPhilosophers() { let scheduler = DiningPhilosophersOperationScheduler()
scheduler.startPhilosopher(0) scheduler.startPhilosopher(3) scheduler.startPhilosopher(2) scheduler.startPhilosopher(1) scheduler.startPhilosopher(4)
}
> philosopher 0 is thinking > philosopher 0 is eating > philosopher 3 is thinking > philosopher 3 is eating > philosopher 2 is thinking > philosopher 1 is thinking > philosopher 4 is thinking > philosopher 3 puts forks > philosopher 0 puts forks > philosopher 2 is eating > philosopher 4 is eating > philosopher 2 puts forks > philosopher 4 puts forks > philosopher 1 is eating > philosopher 1 puts forks
Синхронизация
> philosopher 0 is thinking > philosopher 0 is eating > philosopher 3 is thinking > philosopher 3 is eating > philosopher 2 is thinking > philosopher 1 is thinking > philosopher 4 is thinking > philosopher 3 puts forks > philosopher 0 puts forks > philosopher 2 is eating > philosopher 4 is eating > philosopher 2 puts forks > philosopher 4 puts forks > philosopher 1 is eating > philosopher 1 puts forks
Ф.0
Ф.1
Ф.2
Ф.3Ф.4
Синхронизация
•Задача "Собираем H2O" Building H2O problem
Синхронизация
•Кислород ждет двух потоков водорода
•Водород ждет одного потока водорода и одного - кислорода
Синхронизация
0
Семафор "Кислород"
Кислород Водород
Семафор "Водород"
0
3Барьер
Синхронизация
0
Семафор "Кислород"
Кислород Водород
Семафор "Водород"
0
3Барьер
О
Синхронизация
0
Семафор "Кислород"
Кислород Водород
Семафор "Водород"
0
3Барьер
О
Синхронизация
-1
Семафор "Кислород"
Кислород Водород
Семафор "Водород"
0
3Барьер
ОH
Синхронизация
-1
Семафор "Кислород"
Кислород Водород
Семафор "Водород"
0
3Барьер
О H
Синхронизация
-1
Семафор "Кислород"
Кислород Водород
Семафор "Водород"
-1
3Барьер
О H
Синхронизация
-1
Семафор "Кислород"
Кислород Водород
Семафор "Водород"
-1
3Барьер
О HH
Синхронизация
-1
Семафор "Кислород"
Кислород Водород
Семафор "Водород"
-1
3Барьер
О H H
Синхронизация
-1
Семафор "Кислород"
Кислород Водород
Семафор "Водород"
-2
3Барьер
О H H
Синхронизация
0
Семафор "Кислород"
Кислород Водород
Семафор "Водород"
0
3Барьер
О H H
Синхронизация
0
Семафор "Кислород"
Кислород Водород
Семафор "Водород"
0
3Барьер
О H H
Синхронизация
0
Семафор "Кислород"
Кислород Водород
Семафор "Водород"
0
0Барьер
О H H
Синхронизация
0
Семафор "Кислород"
Кислород Водород
Семафор "Водород"
0
0Барьер
О H H
Синхронизация
0
Семафор "Кислород"
Кислород Водород
Семафор "Водород"
0
3Барьер
Синхронизация
•Решение с использованием NSOperation
Синхронизация
func triggerHydrogenEvent(location: CGPoint) { let sprite = AtomNode.atom(AtomSprite.Hydrogen, location: location) self.addChild(sprite) self.scheduler.addHydrogen(sprite, block: atomBlock(sprite)) }
Синхронизация
class H2OOperationScheduler { let moleculeQueue = NSOperationQueue() // Этот блок вызывается при успешном сборе молекулы H2O var moleculeBlock: (Array<SKSpriteNode>) -> Void init(H2OBlock: (Array<SKSpriteNode>) -> Void) { moleculeQueue.maxConcurrentOperationCount = 1 moleculeBlock = H2OBlock } ... }
Синхронизация
func addHydrogen(node: SKSpriteNode, block: () -> Void) { let currentMolecule = obtainMolecule { (molecule) -> Bool in return molecule.hydrogen < 2 } let operation = AtomOperation(node: node, block: block) currentMolecule.addHydrogen(operation) }
Синхронизация
class H2OOperation: NSOperation { let atomQueue = NSOperationQueue() let moleculeBlock: (Array<SKSpriteNode>) -> Void var nodes = Array<SKSpriteNode>()
... override var ready: Bool { return hydrogen == 2 && oxygen == 1 }
func addHydrogen(operation: AtomOperation) { nodes.append(operation.node) atomQueue.addOperation(operation) hydrogen++ } }
Синхронизация
class H2OOperation: NSOperation { ...
override func main() { atomQueue.suspended = false atomQueue.waitUntilAllOperationsAreFinished() self.moleculeBlock(nodes) } ... }
Синхронизация
•Синхронизация - это непросто
•Синхронизация - это интересно
•Синхронизация - это важно
Синхронизация
•Код примеров:https://github.com/rambler-digital-solutions/synchronization-problems
Синхронизация
• The Little Book of Semaphores Allen B. Downeyhttp://www.greenteapress.com/semaphores/
top related