$30 off During Our Annual Pro Sale. View Details »

Задачи синхронизации: классические и прикладные решения

Egor Tolstoy
September 28, 2015

Задачи синхронизации: классические и прикладные решения

Выступление на Rambler.iOS #4:
Видео: http://www.youtube.com/watch?v=y0UQEioIgTQ
Исходники: https://github.com/rambler-digital-solutions/synchronization-problems

Egor Tolstoy

September 28, 2015
Tweet

More Decks by Egor Tolstoy

Other Decks in Technology

Transcript

  1. Егор Толстой
    Ведущий iOS разработчик @ Rambler&Co
    Задачи синхронизации
    Классические и прикладные решения

    View Slide

  2. Синхронизация
    • Что такое синхронизация
    • Решение проблем синхронизации
    • Классические задачи
    синхронизации

    View Slide

  3. Синхронизация
    • Синхронизация - 

    обеспечение правильной
    совместной работы нескольких
    потоков.

    View Slide

  4. Синхронизация
    Алиса
    Боб
    Поток 1 Поток 2

    View Slide

  5. Синхронизация
    Алиса Боб
    Поток 1 Поток 2

    View Slide

  6. Синхронизация
    Алиса Боб
    Поток 1 Поток 2

    View Slide

  7. Синхронизация
    Алиса
    Боб
    Поток 1 Поток 2

    View Slide

  8. Синхронизация
    • Состояние гонки - 

    работа приложения зависит от
    последовательности вызова
    секции его кода разными
    потоками.

    View Slide

  9. Синхронизация
    func incrementSharedVariable {
    // counter == 0
    counter += 1
    }

    View Slide

  10. Синхронизация
    func incrementSharedVariable {
    // counter == 0
    counter += 1
    }
    Поток 1 Поток 2 Поток 3

    View Slide

  11. Синхронизация
    func incrementSharedVariable {
    // counter == 0
    counter += 1
    }
    Поток 1 Поток 2 Поток 3
    counter ∈ [1,3]

    View Slide

  12. Синхронизация
    • Критическая секция - 

    участок кода, в котором
    одновременно может находиться
    только один поток.

    View Slide

  13. Синхронизация
    func incrementSharedVariable {
    lockSection()
    counter += 1
    unlockSection()
    }

    View Slide

  14. Синхронизация
    Поток 1 Поток 2 Поток 3
    func incrementSharedVariable {
    lockSection()
    counter += 1
    unlockSection()
    }

    View Slide

  15. Синхронизация
    Поток 1 Поток 2 Поток 3
    func incrementSharedVariable {
    lockSection()
    counter += 1
    unlockSection()
    }

    View Slide

  16. Синхронизация
    Поток 1 Поток 2 Поток 3
    func incrementSharedVariable {
    lockSection()
    counter += 1
    unlockSection()
    }

    View Slide

  17. Синхронизация
    Поток 1 Поток 2 Поток 3
    func incrementSharedVariable {
    lockSection()
    counter += 1
    unlockSection()
    }
    counter == 3

    View Slide

  18. Синхронизация
    • Deadlock - 

    несколько потоков блокируют
    выполнение друг друга.

    View Slide

  19. Синхронизация
    • У каждой задачи синхронизации
    есть определенные условия.

    View Slide

  20. Синхронизация
    Алиса Боб
    Поток 1 Поток 2

    View Slide

  21. Синхронизация
    Алиса
    Боб
    Поток 1 Поток 2

    View Slide

  22. Синхронизация
    Алиса
    Боб
    Поток 1 Поток 2

    View Slide

  23. Синхронизация
    Алиса Боб
    Поток 1 Поток 2

    View Slide

  24. Синхронизация
    • Передача сообщений между
    потоками - простое решение
    большинства проблем.

    View Slide

  25. Синхронизация
    Алиса Боб
    а1: Съесть завтрак
    а2: Поработать
    а3: Съесть обед
    а4: Позвонить Бобу
    б1: Съесть завтрак
    б2: Дождаться звонка Алисы
    б3: Съесть обед
    а1 < a2 < a3 < a4 б1 < б2 < б3

    View Slide

  26. Синхронизация
    Алиса Боб
    а1: Съесть завтрак
    а2: Поработать
    а3: Съесть обед
    а4: Позвонить Бобу
    б1: Съесть завтрак
    б2: Дождаться звонка Алисы
    б3: Съесть обед
    а1 < a2 < a3 < a4 б1 < б2 < б3

    View Slide

  27. Синхронизация
    Алиса Боб
    а1: Съесть завтрак
    а2: Поработать
    а3: Съесть обед
    а4: Позвонить Бобу
    б1: Съесть завтрак
    б2: Дождаться звонка Алисы
    б3: Съесть обед
    а1 < a2 < a3 < a4 б1 < б2 < б3
    б3 > б2 > a4 > a3

    View Slide

  28. Синхронизация
    • События считаются
    параллельными, если, изучив код
    программы, мы не можем
    утверждать, какое из них
    произойдет первым.

    View Slide

  29. Синхронизация
    0
    Алиса
    Семафор
    Боб

    View Slide

  30. Синхронизация
    0
    Алиса
    Семафор
    Боб

    View Slide

  31. Синхронизация
    wait
    0
    Алиса
    Семафор
    Боб

    View Slide

  32. Синхронизация
    wait
    -1
    Алиса
    Семафор
    Боб

    View Slide

  33. Синхронизация
    wait
    -1
    Алиса
    Семафор
    Боб

    View Slide

  34. Синхронизация
    signal wait
    -1
    Алиса
    Семафор
    Боб

    View Slide

  35. Синхронизация
    signal wait
    0
    Алиса
    Семафор
    Боб

    View Slide

  36. Синхронизация
    signal wait
    0
    Алиса
    Семафор
    Боб

    View Slide

  37. Синхронизация
    • Семафор создается с начальным
    значением,
    • wait - уменьшает счетчик,
    • signal - увеличивает счетчик.

    View Slide

  38. Синхронизация
    • Задача "Читатели-Писатели" 

    Readers-Writers problem

    View Slide

  39. Синхронизация
    • Любое количество читателей в
    критической секции
    • Единственный писатель в
    критической секции

    View Slide

  40. Синхронизация
    1.
    Семафор
    "комната пуста"
    off
    Выключатель
    "читатели"
    Читатели Писатели
    0

    View Slide

  41. Синхронизация
    1.
    Семафор
    "комната пуста"
    off
    Выключатель
    "читатели"
    Читатели Писатели
    0

    View Slide

  42. Синхронизация
    1.
    Семафор
    "комната пуста"
    off
    Выключатель
    "читатели"
    Читатели Писатели
    0

    View Slide

  43. Синхронизация
    1.
    Семафор
    "комната пуста"
    off
    Выключатель
    "читатели"
    Читатели Писатели
    0

    View Slide

  44. Синхронизация
    1.
    Семафор
    "комната пуста"
    off
    Выключатель
    "читатели"
    Читатели Писатели
    0

    View Slide

  45. Синхронизация
    0.
    Семафор
    "комната пуста"
    on
    Выключатель
    "читатели"
    Читатели Писатели
    1

    View Slide

  46. Синхронизация
    Семафор
    "комната пуста"
    on
    Выключатель
    "читатели"
    Читатели Писатели
    2
    0.

    View Slide

  47. Синхронизация
    Семафор
    "комната пуста"
    on
    Выключатель
    "читатели"
    Читатели Писатели
    2
    -1.

    View Slide

  48. Синхронизация
    Семафор
    "комната пуста"
    on
    Выключатель
    "читатели"
    Читатели Писатели
    2
    -1.

    View Slide

  49. Синхронизация
    Семафор
    "комната пуста"
    on
    Выключатель
    "читатели"
    Читатели Писатели
    2
    -1.

    View Slide

  50. Синхронизация
    Семафор
    "комната пуста"
    on
    Выключатель
    "читатели"
    Читатели Писатели
    2
    -1.

    View Slide

  51. Синхронизация
    Семафор
    "комната пуста"
    on
    Выключатель
    "читатели"
    Читатели Писатели
    1
    -1.

    View Slide

  52. Синхронизация
    Семафор
    "комната пуста"
    off
    Выключатель
    "читатели"
    Читатели Писатели
    0
    0.

    View Slide

  53. Синхронизация
    Семафор
    "комната пуста"
    off
    Выключатель
    "читатели"
    Читатели Писатели
    0
    0.

    View Slide

  54. Синхронизация
    Семафор
    "комната пуста"
    off
    Выключатель
    "читатели"
    Читатели Писатели
    0
    1.

    View Slide

  55. Синхронизация
    • Голод - состояние, в котором
    поток может бесконечно ждать
    своей очереди. 

    А может и дождаться.

    View Slide

  56. Синхронизация
    • Решение с использованием
    NSOperation

    View Slide

  57. Синхронизация
    class OperationScheduler {
    var readerQueue = NSOperationQueue()
    var writerQueue = NSOperationQueue()
    init () {
    // Читатели должны выполняться параллельно
    readerQueue.maxConcurrentOperationCount = MAX
    // Писатели должны выполняться последовательно
    writerQueue.maxConcurrentOperationCount = 1
    }
    ...
    }

    View Slide

  58. Синхронизация
    func addReaderOperation(operation: NSBlockOperation) {
    // Зависим от выполнения всех писателей
    for writerOperation in writerQueue.operations {
    operation.addDependency(writerOperation)
    }
    readerQueue.addOperation(operation)
    }

    View Slide

  59. Синхронизация
    func addWriterOperation(operation: NSBlockOperation) {
    // Все читатели зависят от нового писателя
    for readerOperation in readerQueue.operations {
    readerOperation.addDependency(operation)
    }
    writerQueue.addOperation(operation)
    }

    View Slide

  60. Синхронизация
    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

    View Slide

  61. Синхронизация
    • Задача "Обедающие философы" 

    Dining philosophers problem

    View Slide

  62. Синхронизация
    while(true) {
    think()
    getForks()
    eat()
    putForks()
    }

    View Slide

  63. Синхронизация
    • Не больше одного философа на
    вилку
    • Не допустить deadlock
    • Не допустить голод

    View Slide

  64. Синхронизация
    Ф.5
    Ф.1
    Ф.2
    Ф.3
    Ф.4
    вилка
    1
    вилка
    1
    вилка 1 вилка
    1
    вилка
    1

    View Slide

  65. Синхронизация
    Ф.5
    Ф.1
    Ф.2
    Ф.3
    Ф.4
    вилка
    0
    вилка
    0
    вилка 0 вилка
    0
    вилка
    0

    View Slide

  66. Синхронизация
    Ф.5
    Ф.1
    Ф.2
    Ф.3
    Ф.4
    вилка
    0
    вилка
    0
    вилка 0 вилка
    0
    вилка
    0

    View Slide

  67. Синхронизация
    Ф.5
    Ф.1
    Ф.2
    Ф.3
    Ф.4
    вилка
    0
    вилка
    0
    вилка 0 вилка
    0
    вилка
    0

    View Slide

  68. Синхронизация
    Ф.5
    Ф.1
    Ф.2
    Ф.3
    Ф.4
    вилка
    0
    вилка
    0
    вилка 0 вилка
    0
    вилка
    0
    deadlock :(

    View Slide

  69. Синхронизация
    Ф.5
    Ф.1
    Ф.2
    Ф.3
    Ф.4
    Семафор
    "официант"
    4
    вилка
    1
    вилка
    1
    вилка 1 вилка
    1
    вилка
    1

    View Slide

  70. Синхронизация
    Ф.5
    Ф.1
    Ф.2
    Ф.3
    Ф.4
    Семафор
    "официант"
    3
    вилка
    1
    вилка
    1
    вилка 1 вилка
    1
    вилка
    1

    View Slide

  71. Синхронизация
    Ф.5
    Ф.1
    Ф.2
    Ф.3
    Ф.4
    Семафор
    "официант"
    3
    вилка
    0
    вилка
    1
    вилка 0 вилка
    1
    вилка
    1

    View Slide

  72. Синхронизация
    Ф.5
    Ф.3
    Ф.4
    вилка
    0
    вилка
    вилка 0 вилка
    1
    вилка
    1
    Ф.2
    Семафор
    "официант"
    2
    Ф.1
    1

    View Slide

  73. Синхронизация
    Ф.5
    Ф.3
    Ф.4
    вилка
    0
    вилка
    0
    вилка 0 вилка
    1
    вилка
    1
    Ф.2
    Семафор
    "официант"
    2
    Ф.1

    View Slide

  74. Синхронизация
    Ф.5
    Ф.3
    Ф.4
    вилка
    0
    вилка
    0
    вилка 0 вилка
    1
    вилка
    1
    Ф.2
    Семафор
    "официант"
    1
    Ф.1

    View Slide

  75. Синхронизация
    Ф.5
    Ф.3
    Ф.4
    вилка
    0
    вилка
    0
    вилка 0 вилка
    0
    вилка
    1
    Ф.2
    Семафор
    "официант"
    1
    Ф.1

    View Slide

  76. Синхронизация
    Ф.5
    Ф.3
    Ф.4
    вилка
    0
    вилка
    0
    вилка 0 вилка
    0
    вилка
    1
    Ф.2
    Семафор
    "официант"
    0
    Ф.1

    View Slide

  77. Синхронизация
    Ф.5
    Ф.3
    Ф.4
    вилка
    0
    вилка
    0
    вилка 0 вилка
    0
    вилка
    0
    Ф.2
    Семафор
    "официант"
    0
    Ф.1

    View Slide

  78. Синхронизация
    Ф.5
    Ф.3
    Ф.4
    вилка
    0
    вилка
    0
    вилка 1 вилка
    0
    вилка
    0
    Ф.2
    Семафор
    "официант"
    1
    Ф.1

    View Slide

  79. Синхронизация
    Ф.5
    Ф.3
    Ф.4
    вилка
    0
    вилка
    0
    вилка 0 вилка
    0
    вилка
    0
    Ф.2
    Семафор
    "официант"
    0
    Ф.1

    View Slide

  80. Синхронизация
    Ф.5
    Ф.3
    Ф.4
    вилка
    1
    вилка
    0
    вилка 0 вилка
    0
    вилка
    0
    Ф.2
    Семафор
    "официант"
    1
    Ф.1

    View Slide

  81. Синхронизация
    Ф.5
    Ф.3
    Ф.4
    вилка
    0
    вилка
    0
    вилка 0 вилка
    0
    вилка
    0
    Ф.2
    Семафор
    "официант"
    0
    Ф.1

    View Slide

  82. Синхронизация
    • Решение с использованием
    NSOperation

    View Slide

  83. Синхронизация
    class DiningPhilosophersOperationScheduler {
    var philosopherQueues = [NSOperationQueue]()
    init() {
    for _ in 0...4 {
    let philosopherQueue = NSOperationQueue()
    philosopherQueue.maxConcurrentOperationCount = 1
    philosopherQueues.append(philosopherQueue)
    }
    }
    ...
    }

    View Slide

  84. Синхронизация
    class PhilosopherBlockOperation: NSBlockOperation {
    init(index: Int) {
    super.init()
    addExecutionBlock({
    print("philosopher \(index) is eating")
    sleep(2)
    print("philosopher \(index) puts forks")
    })
    }
    }

    View Slide

  85. Синхронизация
    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)
    }

    View Slide

  86. Синхронизация
    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

    View Slide

  87. Синхронизация
    > 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

    View Slide

  88. Синхронизация
    • Задача "Собираем H2O" 

    Building H2O problem

    View Slide

  89. Синхронизация
    • Кислород ждет двух потоков
    водорода
    • Водород ждет одного потока
    водорода и одного - кислорода

    View Slide

  90. Синхронизация
    0
    Семафор
    "Кислород"
    Кислород Водород
    Семафор
    "Водород"
    0
    3
    Барьер

    View Slide

  91. Синхронизация
    0
    Семафор
    "Кислород"
    Кислород Водород
    Семафор
    "Водород"
    0
    3
    Барьер
    О

    View Slide

  92. Синхронизация
    0
    Семафор
    "Кислород"
    Кислород Водород
    Семафор
    "Водород"
    0
    3
    Барьер
    О

    View Slide

  93. Синхронизация
    -1
    Семафор
    "Кислород"
    Кислород Водород
    Семафор
    "Водород"
    0
    3
    Барьер
    О
    H

    View Slide

  94. Синхронизация
    -1
    Семафор
    "Кислород"
    Кислород Водород
    Семафор
    "Водород"
    0
    3
    Барьер
    О H

    View Slide

  95. Синхронизация
    -1
    Семафор
    "Кислород"
    Кислород Водород
    Семафор
    "Водород"
    -1
    3
    Барьер
    О H

    View Slide

  96. Синхронизация
    -1
    Семафор
    "Кислород"
    Кислород Водород
    Семафор
    "Водород"
    -1
    3
    Барьер
    О H
    H

    View Slide

  97. Синхронизация
    -1
    Семафор
    "Кислород"
    Кислород Водород
    Семафор
    "Водород"
    -1
    3
    Барьер
    О H H

    View Slide

  98. Синхронизация
    -1
    Семафор
    "Кислород"
    Кислород Водород
    Семафор
    "Водород"
    -2
    3
    Барьер
    О H H

    View Slide

  99. Синхронизация
    0
    Семафор
    "Кислород"
    Кислород Водород
    Семафор
    "Водород"
    0
    3
    Барьер
    О H H

    View Slide

  100. Синхронизация
    0
    Семафор
    "Кислород"
    Кислород Водород
    Семафор
    "Водород"
    0
    3
    Барьер
    О H H

    View Slide

  101. Синхронизация
    0
    Семафор
    "Кислород"
    Кислород Водород
    Семафор
    "Водород"
    0
    0
    Барьер
    О H H

    View Slide

  102. Синхронизация
    0
    Семафор
    "Кислород"
    Кислород Водород
    Семафор
    "Водород"
    0
    0
    Барьер
    О H H

    View Slide

  103. Синхронизация
    0
    Семафор
    "Кислород"
    Кислород Водород
    Семафор
    "Водород"
    0
    3
    Барьер

    View Slide

  104. Синхронизация
    • Решение с использованием
    NSOperation

    View Slide

  105. Синхронизация
    func triggerHydrogenEvent(location: CGPoint) {
    let sprite = AtomNode.atom(AtomSprite.Hydrogen, location: location)
    self.addChild(sprite)
    self.scheduler.addHydrogen(sprite, block: atomBlock(sprite))
    }

    View Slide

  106. Синхронизация
    class H2OOperationScheduler {
    let moleculeQueue = NSOperationQueue()
    // Этот блок вызывается при успешном сборе молекулы H2O
    var moleculeBlock: (Array) -> Void
    init(H2OBlock: (Array) -> Void) {
    moleculeQueue.maxConcurrentOperationCount = 1
    moleculeBlock = H2OBlock
    }
    ...
    }

    View Slide

  107. Синхронизация
    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)
    }

    View Slide

  108. Синхронизация
    class H2OOperation: NSOperation {
    let atomQueue = NSOperationQueue()
    let moleculeBlock: (Array) -> Void
    var nodes = Array()
    ...
    override var ready: Bool {
    return hydrogen == 2 && oxygen == 1
    }
    func addHydrogen(operation: AtomOperation) {
    nodes.append(operation.node)
    atomQueue.addOperation(operation)
    hydrogen++
    }
    }

    View Slide

  109. Синхронизация
    class H2OOperation: NSOperation {
    ...
    override func main() {
    atomQueue.suspended = false
    atomQueue.waitUntilAllOperationsAreFinished()
    self.moleculeBlock(nodes)
    }
    ...
    }

    View Slide

  110. Синхронизация
    • Синхронизация - это непросто
    • Синхронизация - это интересно
    • Синхронизация - это важно

    View Slide

  111. Синхронизация
    • Код примеров:

    https://github.com/rambler-
    digital-solutions/synchronization-
    problems

    View Slide

  112. Синхронизация
    • The Little Book of Semaphores

    Allen B. Downey


    http://www.greenteapress.com/
    semaphores/

    View Slide