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

Concurrency Models

Concurrency Models

My talk on rannts #20 (25.08.2018)

Sergey Arkhipov

August 25, 2018
Tweet

More Decks by Sergey Arkhipov

Other Decks in Programming

Transcript

  1. Модели конкуренции
    Сергей Архипов

    View Slide

  2. Заключение
    — Идеального нет
    — Плохого тоже нет
    — asyncio — ничего особенного
    — Надо на досуге почитать про libuv и BeamVM
    — Самое время посмотреть на другие модели и языки

    View Slide

  3. View Slide

  4. — Конкурентность — это когда несколько независимых
    подпроцессов логически конкурируют за машинное
    время.
    — Параллелизм — это когда несколько независимых
    подпроцессов физически конкурируют за машинное
    время.

    View Slide

  5. Вытесняющая кооперативная
    конкуренция
    — Вытесняющая (preemptive) — это когда у нас
    планировщик может сам решить поставить процесс
    на паузу и приступить к другому
    — Кооперативная (cooperative) — это когда один
    процесс может сказать планировщику, что он пока
    закончил работу, и когда он может продолжить

    View Slide

  6. Потоки

    View Slide

  7. View Slide

  8. View Slide

  9. — Вытесняющая
    многозадачность
    — Отлично работает с
    нативным кодом
    — Идеально для
    числодробления
    — Планировщик ничего
    не знает о внешнем
    мире
    — Мы не можем
    эффективно
    подсказывать
    планировщику
    — Нет кооперации

    View Slide

  10. Event loop’ы

    View Slide

  11. View Slide

  12. View Slide

  13. View Slide

  14. View Slide

  15. write(2)

    View Slide

  16. write(2)
    run_in_executor()

    View Slide

  17. View Slide

  18. View Slide

  19. l.acquire() l.acquire() l.acquire() l.acquire()
    import threading
    l = threading.RLock()

    View Slide

  20. l.acquire() l.acquire() l.acquire() l.acquire()
    import threading
    l = threading.RLock()
    monkey.patch_all()

    View Slide

  21. View Slide

  22. View Slide

  23. async with a, b:

    async with b, a:

    View Slide

  24. Deadlock! Deadlock!

    View Slide

  25. — Эффективная
    кооперация на I/O
    — Меньше заморочек с
    синхронизацией
    — Проще дебажить
    — Можно хорошенько
    замазать колбеки
    синтаксическим
    сахаром
    — Нет вытесняющей
    многозадачности
    — Одноядерность
    — Постоянно лезут
    костыли-потоки
    — Требует
    синтаксического
    сахара, чтобы можно
    было нормально
    работать

    View Slide

  26. CSP / Actors

    View Slide

  27. View Slide

  28. crawlera-headless-proxy
    1. if sess.id == "" && sess.creator == "" {
    2. sess.cond.L.Lock()
    3. if sess.id == "" && sess.creator == "" {
    4. sess.id = ""
    5. sess.creator = rstate.ID
    6. sess.cond.L.Unlock()
    7. req.Header.Set("X-Crawlera-Session", "create")
    8.
    9. return req, nil
    10. }
    11. sess.cond.L.Unlock()
    12. }
    13.
    14. sess.cond.L.Lock()
    15. defer sess.cond.L.Unlock()
    16. for sess.id == "" {
    17. sess.cond.Wait()
    18. }

    View Slide

  29. crawlera-headless-proxy
    1. if sess.creator == rstate.ID {
    2. sess.cond.L.Lock()
    3. defer sess.cond.L.Unlock()
    4.
    5. sess.creator = ""
    6. sess.id = resp.Header.Get("X-Crawlera-Session")
    7.
    8. sess.cond.Broadcast()
    9. }

    View Slide

  30. crawlera-headless-proxy
    1. sess.cond.L.Lock()
    2. defer sess.cond.L.Unlock()
    3.
    4. if ctx.Req.Header.Get("X-Crawlera-Session") == sess.id {
    5. sess.id = ""
    6. }
    7. for !(sess.creator == "" || sess.creator == rstate.ID || sess.id != "") {
    8. sess.cond.Wait()
    9. }
    10.
    11. ...
    12.
    13. if newResp, err := rstate.DoCrawleraRequest(s.httpClient, req); err == nil
    && newResp.Header.Get("X-Crawlera-Error") == "" {
    14. sess.id = newResp.Header.Get("X-Crawlera-Session")
    15. sess.cond.Broadcast()
    16. return newResp
    17. }
    18.
    19. sess.cond.Signal()
    20.
    21. return resp

    View Slide

  31. crawlera-headless-proxy
    1. sessionID := mgr.getSessionID(false)
    2. switch value := sessionID.(type) {
    3. case string:
    4. req.Header.Set("X-Crawlera-Session", sessionID)
    5. case chan <- string:
    6. req.Header.Set("X-Crawlera-Session", "create")
    7. s.sessionChans.Set(rstate.ID, sessionIDChan, sessionsChansTTL)
    8. }
    9.
    10. return req, nil

    View Slide

  32. crawlera-headless-proxy
    1. if item := s.sessionChans.Get(rstate.ID); item != nil {
    2. sessionIDChan := item.Value().(chan <- string)
    3. defer close(sessionIDChan)
    4.
    5. sessionID := resp.Header.Get("X-Crawlera-Session")
    6. if !item.Expired() {
    7. sessionIDChan <- sessionID
    8. }
    9. }
    10.
    11. return resp

    View Slide

  33. crawlera-headless-proxy
    1. brokenSessionID := ctx.Req.Header.Get("X-Crawlera-Session")
    2. mgr.getBrokenSessionChan() <- brokenSessionID
    3. if item := s.sessionChans.Get(rstate.ID); item != nil {
    4. close(item.Value().(chan <- string))
    5. }
    6.
    7. sessionID := mgr.getSessionID(true)
    8. switch value := sessionID.(type) {
    9. case chan <- string:
    10. ctx.Req.Header.Set("X-Crawlera-Session", sessionID)
    11. resp, err := rstate.DoCrawleraRequest(s.httpClient, ctx.Req)
    12. if err != nil || resp.Header.Get("X-Crawlera-Error") != "" {
    13. mgr.getBrokenSessionChan() <- sessionID
    14. }
    15. return resp
    16. default:
    17. defer close(sessionIDChan)
    18. ctx.Req.Header.Set("X-Crawlera-Session", "create")
    19. resp, err := rstate.DoCrawleraRequest(s.httpClient, ctx.Req)
    20. if err == nil && resp.Header.Get("X-Crawlera-Error") == "" {
    21. sessionID := resp.Header.Get("X-Crawlera-Session")
    22. sessionIDChan <- sessionID
    23. }
    24. return resp
    25. }

    View Slide

  34. crawlera-headless-proxy
    1. for {
    2. select {
    3. case feedback := <-s.requestIDChan:
    4. if s.id == "" {
    5. s.requestNewSession(feedback)
    6. } else {
    7. feedback.channel <- s.id
    8. }
    9. case brokenSession := <-s.brokenSessionChan:
    10. if s.id == brokenSession {
    11. s.sessionsToDelete <- brokenSession
    12. s.id = ""
    13. }
    14. }

    View Slide

  35. crawlera-headless-proxy
    1. newSessionChan := make(chan string, 1)
    2. feedback.channel <- (chan <- string(newSessionChan))
    3.
    4. timeAfter := s.getTimeoutChannel(feedback.retry)
    5. for {
    6. select {
    7. case brokenSession := <-s.brokenSessionChan:
    8. if s.id == brokenSession {
    9. s.sessionsToDelete <- brokenSession
    10. s.id = ""
    11. }
    12. case newSession, notClosed := <-newSessionChan:
    13. if notClosed {
    14. s.id = newSession
    15. }
    16. return
    17. case <-timeAfter:
    18. log.Debug("Timeout in waiting for the new session.")
    19. return
    20. }
    21. }

    View Slide

  36. View Slide

  37. — Простая
    конкурентность
    — Вытесняющая
    кооперативная
    конкурентность
    — Хорошая
    масштабируемость в
    пределах ноды
    — Везде все разное
    — Не такой богатых набор
    лучших практик
    — Чувствительность к
    дизайну приложения
    — Каскадные эффекты

    View Slide

  38. View Slide