Многозадачност и сериализация

„ Програмиране с Python“, ФМИ

ДимиНитър Стефколай анБаКънчев Дийимитровски

26.05.2008

Многозадачност

Всички искат да са като Наполеон — да правят поне две неща едновременно Операционните системи ни дават два подхода:

fork

Пример с fork

import os
import time

def log(msg): print "\n* %s" % msg

orders = 0
while True:
    order = raw_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 '%s' is ready!" % order)
        break
    else:
        log("Roger that '%s'(%d). Please, wait in quiet desperation." % (order, orders))
        orders += 1

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

Против:

За:

Нишки

Нишки в python

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

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

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 '%s' is ready!" % self.order)

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

Вечерящи философи, опростен вариант

import random
import time
import threading

taken = False

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

    def log(self, msg): print "%s: %s" % (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
import time
import threading

bathroom = threading.Lock()

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

    def log(self, msg): print "%s: %s" % (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
import random
import 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 "...(%s) waiting for an oven" % self.name
            ovens.acquire()
            print "--> (%s) Cooking..." % self.name
            time.sleep(random.random() * 10)
            ovens.release()
            print "<-- (%s) Serving..." % self.name
            time.sleep(random.random() * 4)


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

threading.Event

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

threading.Condition

threading.local

Още един проблем

Решение

Pickle

pickle е модул, който може да сериализира прости Python обекти. Приема отворен за писане файл file и Python обект object. Записва обекта в файла. Приема отворен за четене файл и прочита един обект, който е и резултат от функцията

Пример с pickle

from __future__ import with_statement
import pickle

with open('/home/bilbo/foo.txt', 'w') as file:
	pickle.dump("The answer", file)
	pickle.dump(["spam", "eggs", "ham"], file)

with open('/home/bilbo/foo.txt', 'r') as file:
	print pickle.load(file)
	print pickle.load(file)

Какво представлява сериализацията?

import pickle

serializedList = pickle.dumps(["spam", "eggs", "ham"])
print repr(serializedList)

"(lp0\nS'spam'\np1\naS'eggs'\np2\naS'ham'\np3\na."

Какво може да се сериализира?

Сериализиране на един обект два пъти

Ако сериализирате сложна структура, в която един обект присъства два пъти (по адрес), pickle ще го сериализира еднократно.

import pickle

primes = [2, 3, 5, 7, 11, 13]
answer = 42
things = [primes, answer, primes]
print things[0] is things[2] # True

serialized = pickle.dumps(things)
newThings = pickle.loads(serialized)
print newThings[0] is newThings[2] # True

print newThings[0] is things[0] # False

Pickler и Unpickler

Имате достъп до същата функционалност под формата на класове:

from __future__ import with_statement
import pickle

with open('/home/aquarius/foo.txt', 'w') as file:
	pickler = pickle.Pickler(file)
	pickler.dump('Under the Violet Moon')
	pickler.dump('Soldier of Fortune')
	pickler.dump('Colubrid on the Tree')

with open('/home/aquarius/foo.txt') as file:
	unpickler = pickle.Unpickler(file)
	print unpickler.load()
	print unpickler.load()
	print unpickler.load()

cPickle

cPickle e реализация на pickle на C, което я прави доста по-бърза.
import cPlickle as pickle
...

pickle с/у cPickle

За сравнение:

import pickle
import cPickle
from timeit import Timer

fancy = [[x ** 2 for x in range(0, 1000)], ["spam", "ham", "eggs"], 
			["-" * i for i in range(0, 1000)],]

def pickleWith(pickler):
	serialized = pickler.dumps(fancy)
	restored = pickler.loads(serialized)

print "pickle: ", Timer("pickleWith(pickle)", "from __main__ import *").timeit(50)
print "cPickle: ", Timer("pickleWith(cPickle)", "from __main__ import *").timeit(50)

# pickle:  3.28504800797
# cPickle:  0.722439050674

shelve

Пример със shelve

import shelve

db = shelve.open('/home/bilbo/foo.db')
db['name'] = 'Mityo the Python'
db['age'] = 33
db['favouriteBands'] = ["Blackmore's Night", "Deep Purple", "Rainbow"]
db['favouriteSong'] = "Colubrid on the Tree"
db.close()

db = shelve.open('/home/bilbo/foo.db')
print db.keys()
print db['name']
print db['favouriteBands']
print db['favouriteSong']
db.close()

Още въпроси?