Много задачи, един процесор. Печелим ли нещо от паралелизация?
А какво става, когато имаме много ядра?
Прави нещата по-неприятни
fork
създава ново копие на програмата, която изпълняваме#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
import os print("before") if os.fork(): print("father") else: print("son") print("both")
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 0 == pid: time.sleep(3) log("Order '{0}' is ready!".format(order)) break else: log("Roger that '{0}'({1}). Please, wait in quiet desperation.".format(order, orders)) orders += 1
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
Против:
За:
threading
или
run
start
import threading def f(name): print("Hello from {0}".format(name)) thread = threading.Thread(target=f, args=('Bob',)) thread.start() thread.join()
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()
ни връща Lock
обектacquire()
ни гарантира, че само ние притежаваме този Lockrelease()
освобождава Lock-а и разрешава някой друг да го заключи с acquire()
acquire()
докато 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()
Lock
-оподбни обекти от threading
са и context manager-и with
ни се гарантира викането на acquire()
преди и на release()
след блокаwith bathroom: self.log("--> (entered the bathroom)") time.sleep(random.random()) self.log("<-- (left the bathroom)")
Или още по-добре
v
P(v)
— чакай докато v > 0
, след което v -= 1
V(v)
— v += 1
Предложени от Едсгер Дейкстра (Едсгар Дийкстра?)
threading.Semaphore(k)
ни връща семафор с интерфейс като на Lock
и стойност k
acquire()
стойността се намалява с 1release()
стойността се увеличава с 10
, acquire()
спи, докато някой не я увеличи с release()
Lock()
е еквивалентен на Semaphore(1)
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()
wait()
блокира докато събитието не се случиset()
„случва“ събитиетоwait()
— чака, докато някоя баба не произведе нова баницаnotify()
— това трябва да каже една баба, която е опекла баница. Ще събуди някой от чакащите. Ако няма чакащи няма да направи нищо.notifyAll()
— ще събуди всички чакащиrelease()
и acquire()
работят върху вътрешен за Condition
Lock, който може да се подаде при конструиранеwait
и notify
работят само ако владеем вътрешния Lock
threading.local()
multiprocessing модулът
threading
, но за процеси Semaphore
, Lock
, RLock
, Condition
, Event
Queue
, Pipe
Value
, Array
) от елементарни данни (int/float/byte/…
) и ctypes
структури Manager
Pool
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()
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)
python3.0 test.py 8 python3.0 test.py 7 # WTF??? - защо се държи странно
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)
Позволява създаването на споделени обекти
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]
Process.__init__()
трябва да са „picklable“ if __name__ == '__main__'
import
Още интересни неща на