Parallelism
Операционните системи ни дават два подхода:
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 или
runstart
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)")
Или още по-добре
vP(v) — чакай докато v > 0, след което v -= 1V(v) — v += 1Предложени от Едсгер Дейкстра (Едсгар Дийкстра?)
threading.Semaphore(k) ни връща семафор с интерфейс като на Lock и стойност kacquire() стойността се намалява с 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 работят само ако владеем вътрешния Lockthreading.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 Още интересни неща на