10. Конкурентност

10. Конкурентност

10. Конкурентност

20 април 2015

Ама първо

Предадените идеи за проекти са < 10

Към момента преподавателския екип е по-многоброен от изявилите желание да завършат курса в редовната сесия

Крания срок е тази неделя, не се мотайте

Писането на идеите за проекти не е лесно

Ама все пак се постарайте

Предавайте идеите си като решения на задачата

Идеите пратени по мейл не важат

ОК е да copy-paste-нете мейла си като решение на задачата

В сряда

Ще имате и истинско домашно, за да не страдате.

Конкурентност

Що е то?

Когато две изчисления се случват едновременно

Когато две изчисления нямат ясно дефинирана последователност на изпълнение.

Демек…?

Паралелизъм?

Това, за което повечето хора си мислят, когато им говориш за конкурентност.

Две изчисления, които реално се изпълняват едновременно

Предполага наличието на много ядра/процесори, така че няколко задачи наистина да вървят паралелно

Подходи

В Python

Видове нишки

Global Interpreter Lock

Един python процес винаги таботи върху едно ядро

Досадно е, но уви спестява един ред други проблеми

Възможно е дори реферирането на променлива в няколко нишки едновременно да създаде проблем(защо?)

fork

Пример с fork (на C)

#include <stdio.h>

int main()
{   
    printf("before\n");
    if (fork())
        printf("father\n");
    else
        printf("son\n");
    printf("both\n");
}


before
son
both
father
both

Пример с fork (на Python)

import os

print("before")
if os.fork():
    print("father")
else:
    print("son")
print("both")

В картинки

В картинки

В картинки

В картинки

В картинки

По-сложен пример с fork

import os
import time

def log(msg): print("\n* " + msg)

orders = 0
while True:
    order = input('Enter order: ')
    if not order: continue
    if order in ('q', 'x', 'quit', 'exit'): break
    pid = os.fork()
    if pid == 0:
        time.sleep(3)
        log("Order '{0}' is ready!".format(order))
        break
    else:
        log("Roger that '{0}'({1}). Please, wait.".format(order, orders))
        orders += 1

Синхронизация при fork

import os

pid = os.fork()
if pid == 0:
    os.execlp('date', 'date')
else:
    status = os.wait()
    print("Father: son has finished {0}".format(status))

можем да ползваме и сигнали import signal

Предимства и недостатъци на fork

Против:

За:

  • Стабилност
  • Синът е независим - ако омаже нещо, бащата няма да пострада

Нишки

Създаване на нова нишка

даваме функция с параметри към нея

или

  • Нишка — наследник на threading.Thread
  • Код — в метода run
  • Създаване и изпълнение на кода паралелно — метод start

Пример с нишки

import threading

def f(name):
    print("Hello from {0}".format(name))

thread = threading.Thread(target=f, args=('Bob',))
thread.start()
thread.join()

Пример с нишки(2)

import threading
import time

orders = 0

class Chef(threading.Thread):
    def __init__(self, order):
        self.order = order
        threading.Thread.__init__(self)
    def run(self):
        time.sleep(3)
        log("Order '{0}' is ready!".format(self.order))

while True:
    order = input('Enter order: ')
    if not order: continue
    if order in ('q', 'x', 'quit', 'exit'): break
    chef = Chef(order)
    chef.start()
    log("Roger that '{0}'. Please, wait in quiet desperation.".format(order))
    orders += 1

Вечерящи философи

import random, time, threading

taken = False
class Philosopher(threading.Thread):
    def __init__(self, name):
        super().__init__(); self.name = name

    def log(self, msg): print("{0}: {1}".format(self.name, msg))
    def eat(self): time.sleep(random.random())
    def ponder(self): time.sleep(random.random())

    def refresh(self):
        global taken
        self.log("Please excuse me...");
        while taken: pass;
        taken = True; self.log("--> (entered the bathroom)")
        time.sleep(random.random())
        taken = False; self.log("<-- (left the bathroom)")

    def run(self):
        while True:
            self.eat(); self.ponder(); self.refresh()

Критични секции

threading.Lock

Философите, отново

import random, time, threading

bathroom = threading.Lock()

class Philosopher(threading.Thread):
    def __init__(self, name):
        super().__init__(); self.name = name

    def log(self, msg): print("{0}: {1}".format(self.name, msg))
    def eat(self): time.sleep(random.random())
    def ponder(self): time.sleep(random.random())

    def refresh(self):
        self.log("Please excuse me...")
        bathroom.acquire(); self.log("--> (entered the bathroom)")
        time.sleep(random.random())
        bathroom.release(); self.log("<-- (left the bathroom)")

    def run(self):
        while True:
            self.eat(); self.ponder(); self.refresh()

with и обекти с acquire и release

with bathroom:
    self.log("--> (entered the bathroom)")
    time.sleep(random.random())
    self.log("<-- (left the bathroom)")

Модерно строителство, модерни ресторанти и семафори

А aко има повече от една тоалетна в сградата?

Или още по-добре

  • Ресторант с 10 човека
  • Всеки е едновременно готвач и сервитьор
  • 5 фурни

Семафори в орехова черупка

threading.Semaphore

Ресторанта на Грибоедов

import threading, random, time

ovens = threading.Semaphore(5)

class WaiterChef(threading.Thread):
    def __init__(self, name):
        super(WaiterChef, self).__init__()
        self.name = name

    def run(self):
        while True:
            print("...({0}) waiting for an oven".format(self.name))
            ovens.acquire()
            print("--> ({0}) Cooking...".format(self.name))
            time.sleep(random.random() * 10)
            ovens.release()
            print("<-- ({0}) Serving...".format(self.name))
            time.sleep(random.random() * 4)


for _ in range(0, 10):
  WaiterChef(_).start()

threading.Event

Коледа на село

threading.Condition

threading.local

multiprocessing

multiprocessing модулът

multiprocessing - пример

from multiprocessing import Process
import os

def info(title):
    print(title)
    print('module name:', __name__)
    print('parent process:', os.getppid())
    print('process id:', os.getpid())

def f(name):
    info('function f')
    print('hello', name)

if __name__ == '__main__':
    info('main line')
    p = Process(target=f, args=('bob',))
    p.start()
    p.join()

multiprocessing - пример с обща памет

from multiprocessing import Process, Value

def f(n):
    # work
    v = n.value
    for x in range(0, 30000): x=x+2
    n.value = v + 1
    # work

if __name__ == '__main__':
    num = Value('i', 0)
    processes = [Process(target=f, args=(num,)) for i in range(0, 10)]
    for p in processes:
        p.start()
    for p in processes:
        p.join()
    print(num.value)

multiprocessing - Lock

Lock - осигурява че само един процес може да го държи

from multiprocessing import Process, Value, Lock

def f(n, lock):
    # work
    lock.acquire()
    v = n.value
    for x in range(0, 30000): x=x+2
    n.value = v + 1
    lock.release()
    # work

if __name__ == '__main__':
    num = Value('i', 0)
    lock = Lock()
    processes = [Process(target=f, args=(num, lock)) for i in range(0, 10)]
    for p in processes:
        p.start()
    for p in processes:
        p.join()
    print(num.value)

multiprocessing - Manager

Позволява създаването на споделени обекти

from multiprocessing import Process, Manager

def f(d, l):
    d[1] = '1'
    d['2'] = 2
    d[0.25] = None
    l.reverse()

if __name__ == '__main__':
    manager = Manager()
    d, l = manager.dict(), manager.list(range(10))
    p = Process(target=f, args=(d, l))
    p.start()
    p.join()
    print(d, l)

{0.25: None, 1: '1', '2': 2}
[9, 8, 7, 6, 5, 4, 3, 2, 1, 0]

multiprocessing - неща, за които трябва да се внимава под Windows

Въпроси?