Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Concurrency Models

Concurrency Models

My talk on rannts #20 (25.08.2018)

A8d8ca813a744866b9f85ea1cefb5813?s=128

Sergey Arkhipov

August 25, 2018
Tweet

Transcript

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

  2. Заключение — Идеального нет — Плохого тоже нет — asyncio

    — ничего особенного — Надо на досуге почитать про libuv и BeamVM — Самое время посмотреть на другие модели и языки
  3. None
  4. — Конкурентность — это когда несколько независимых подпроцессов логически конкурируют

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

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

  7. None
  8. None
  9. — Вытесняющая многозадачность — Отлично работает с нативным кодом —

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

  11. None
  12. None
  13. None
  14. None
  15. write(2)

  16. write(2) run_in_executor()

  17. None
  18. None
  19. l.acquire() l.acquire() l.acquire() l.acquire() import threading l = threading.RLock()

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

  21. None
  22. None
  23. async with a, b: … async with b, a: …

  24. Deadlock! Deadlock!

  25. — Эффективная кооперация на I/O — Меньше заморочек с синхронизацией

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

  27. None
  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. }
  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. }
  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
  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
  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
  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. }
  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. }
  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. }
  36. None
  37. — Простая конкурентность — Вытесняющая кооперативная конкурентность — Хорошая масштабируемость

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