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

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

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

24 април 2013

Паралелизация vs конкурентност

Всъщност каква е разликата?

Moore's law

А какво става, когато имаме много ядра?

Подходи

В Python

Видове нишки

Global Interpreter Lock

Прави нещата по-неприятни

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))

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

Против:

За:

Нишки

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

или

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

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)")

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

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

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

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

Въпроси?