rambler.ios #4: Задачи синхронизации. Классические и...

Post on 14-Apr-2017

1.159 Views

Category:

Technology

0 Downloads

Preview:

Click to see full reader

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