събота, 17 януари 2009 г.

Нишки в Python

„ Програмиране с Python“, ФМИ
Стефан Кънев & Николай Бачийски
28.05.2007г.
Многозадачност

Класически проблем е да накараме една програма да върши няколко неща едновременно. Има два стандартни подхода в повечето езици - fork-ове и нишки. Както можете да очаквате, те се срещат в Python в модулите os и threading.
fork

fork е функция, чието изпълнение създава ново копие на програмата, което продължава от същото място където е извикана функцията. Досега заделените ресурси са общи за двата процеса, докато новите са локални. Създава се нов процес на ниво операционна система. В родителския процес функцията връща id-то на дъщерния, докато във дъщерния връща 0.


import os

print "pre-forking"
pid = os.fork()
print "post-forking", pid

pre-forking
post-forking 0
post-forking 6450

fork (2)

Отделни процеси могат да си комуникират по-успешно с други функции в модула os. Като цяло fork е твърде примитивен подход когато имате нужда от конкурентност в приложението и в почти всички случаи е много по-добре да използвате по-високо ниво на абстракция - нишки.
thread и threading

Python предлага два модула за работа с нишки - thread и threading. Втория предлага по-високо ниво на абстракция и по-удобен интерфейс. Съответно е по-логичния избор от двата модула, ако нямате смислена причина да предпочетете thread.
threading.Thread

Класът Thread ви дава прост механизъм да изпълнявате код в нова нишка. Нужно е да предоставите метод run, който да съдържа кода на нишката. След това я инстанцирате и викате методът и start.


import threading

class MyThread(threading.Thread):
def run(self):
for i in range(0, 10000): print i

thread = MyThread()
thread.start()
for i in range(0, 10000): print -i

theading.Lock

Стандартен проблем при конкуренти задачи са "критични секции" от програмата - такива, които трябва да бъдат изпълнявани само веднъж в даден момент. Това може да се постигне с класът Lock. Неговите инстанции имат два метода - acquire() и block(). Класът вътре пази флаг дали lock-а е свободен или зает. Ако acquire() се извика на свободен lock, флага се вдига и lock-а става зает. Ако acquire() се извика на зает lock, метода блокира докато lock-а не се освободи. Извикването на release() освобождава lock-а.
theading.Lock (2)


import threading

lock = threading.Lock()

class Gatherer(threading.Thread):
def run(self):
while True:
foundStuff = gather()
lock.acquire()
for thing in foundStuff: report(thing)
lock.release()


g1, g2 = Gatherer(), Gatherer()
g1.start()
g2.start()

threading.Lock (3)

Можете да използвате with с Lock.


from __future__ import with_statement
import threading

lock = threading.Lock()
with lock:
print "Do stuff"

threading.Semaphore

Semaphore представлява механизъм за разрешаване на конкурентен достъп, много подобен на Lock разликата е там, че вместо флаг, вътрешно се пази брояч. acquire() го намалява, докато release() го увеличава. acquire() се разблокира, когато брояча е по-голям от нула и блокира, ако той е нула.
threading.Event

Event е проста комуникация между нишки, в която няколко нишки изчакват една да завърши с някакъв резултат. Нишките които изчакват викат метода wait() и изпълнението им блокира. Нишката, която продуцира резултата извиква метода set(). В този момент всички нишки които чакат продължават изпълнението си.
threading.Condition

Condition е много сходен с Lock, но дава няколко нови метода. wait() освобождава lock-а, след което блокира докато не се събуди с извикване на notify() или notifyAll(). Като се "събуди", отново взема lock-а. notify() събужда една нишка, заспала с wait(). notifyAll() събуджа всички.


# Consume one item
cv.acquire()
while not an_item_is_available():
cv.wait()
get_an_available_item()
cv.release()

# Produce one item
cv.acquire()
make_an_item_available()
cv.notify()
cv.release()


threading.local

Инстанциите на local представляват обекти, които са "локални" за дадена нишка. Тяхните атрибути са уникални за всяка нишка. Например:


import threading
foo = threading.local()

foo.blah = 1

class MyThread(threading.Thread):
def run(self):
if (hasattr(foo, 'blah')): print "MyThread sees foo.blah"
foo.blah = 4
print "MyThread(foo.blah) = ", foo.blah

MyThread().start()
print "BaseThread(foo.blah) = ", foo.blah

Няма коментари:

Публикуване на коментар