znalazlem takie opisy, ale mimo kilku-krotnemu przeczytaniu
i przeanalizowaniu nadal nie kumam tego
1.) http://www.devshed.com/c/a/Python/Basic-Threading-in-Python/
2.) http://linuxgazette.net/107/pai.html
3.) http://docs.python.org/lib/module-threading.html
4.) http://heather.cs.ucdavis.edu/~matloff/Python/PyThreads.pdf
Czy macie jakies linki do dobrych opisow gdzie bedzie w sposob jasny
(jak dla 'debila')
wytlumaczony jak dzialaja 'watki' w pythonie?
pozdrawiam
p.s.
import threading
class MyThread(threading.Thread):
def __init__(self):
threading.Thread.__init__(self)
def run(self):
# Tutaj sobie coś robisz, np. odwracasz macierz.
# Ten kawałek kodu będzie wykonany w oddzielnym wątku.
matrix = Matrix()
matrix.odwracaj_se_przez_najblizsze_trzy_dni()
# Odpalanie trzech wątków robiacych "to samo". Wsystkie tsy będą działały,
# nazwijmy to - równolegle, a odpalenie każdego z nich przekazuje sterowanie
# do głównego wątku bez czekania aż podwątek zakończy swój marny żywot.
thread1 = MyThread()
thread1.run()
thread2 = MyThread()
thread2.run()
thread3 = MyThread()
thread3.run()
To tyle.
Pozdrawiam
Beorn
--
Daniel 'Beorn' Mróz <be...@alpha.pl> http://127.0.0.1/beorn
[GIT d s:- a-@ C++++ UL++++$ P+ L++++ E--- W+ N+++ o? K- w---]
[O- M- V! PS+ PE++ Y+ PGP++ t- 5 X R !tv b+ DI D++ G++ e h*]
[ r++ y+ ]
obrazkowo to ma wygladac tak: tablica[a1, a2,...a50] i jakas funkcja()
biore kolejne wartosci z tablicy i wrzucam je do funkcji, zalezy mi na
czasie
i dlatego pomyslalem ze powinny dzialac caly czas 8 funkcji() (jedna
uruchomiona
funkcja dziala przez okolo 8min, wiec 8*50/60>6h, tyle to trwa jak
zapuszczam
jeden po drugim bez watkow)
nie wiem czy jest to jasne
> zalezy mi na tym aby nonstop byly aktywne tylko 8 definicji z
> kolejnymi zmiennymi z
> tej tablicy tak dlugo az dojdzie do ostatniej wartosci z tablicy
...i pilnujesz żeby nie było uruchomione więcej jak 8 wątków na raz.
> obrazkowo to ma wygladac tak: tablica[a1, a2,...a50] i jakas funkcja()
> biore kolejne wartosci z tablicy i wrzucam je do funkcji, zalezy mi na
> czasie
> i dlatego pomyslalem ze powinny dzialac caly czas 8 funkcji() (jedna
> uruchomiona
> funkcja dziala przez okolo 8min, wiec 8*50/60>6h, tyle to trwa jak
> zapuszczam jeden po drugim bez watkow)
Hmmm.... Nie wiem czy zyskasz w ten sposób jakieś drastyczne przyspieszenie.
Zależy co ta funkcja robi i jak bardzo obciąża zasoby skoro jej wykonanie
trwa 8 minut. Może się skończyc tak, że uruchomienie 8 wątków tylko całość
spowolni jeśli funkcja np. nie wykonuje żadnych operacji I/O tylko jedzie
algebrą po danych.
> nie wiem czy jest to jasne
Jasne. Niejasne nadal jest w czym masz problem.
Ja widze tutaj dwa rozwiązania: proste i bardziej złożone.
* Rozwiązanie proste
Aplikacja, czyli wątek główny, zajmuje się pobieraniem danych z tablicy,
uruchamianiem wątków oraz pilnowaniem żeby z ich liczbą nie przesadzić
(max 8 np). Wątki mogą informować takiego nadzorcę, że już zakończyły
rzeźbienie i się zamknęły tak, aby mógł on odpalić kolejny z nową porcją
danych. Sam nadzorca może też trzymać wątki w innej tablicy, okresowo
sprawdzać czy któryś z nich sie nie zakończył i uzupełniać braki do 8.
* Rozwiązanie nieco bardziej złożone
Nadzorca uruchamia 8 wątków (tzw. workerów), które są uruchomione przez
cały czas. Następnie przekazuje im dane z tablicy do obróbki i
opcjonalnie sprawdza czy którys z nich nie poszedł w krzaki (zdarza się)
na co odpowiednio reaguje (zapis w logu, cofnięcie nieobrobionych danych
do ponownego przetworzenia) i uzupełnia brak w wątkach. Każdy worker musi
albo informować nadzorcę o tym, że zakończył miętosić dane i czeka na
nową porcję, lub udostępniać jakiś interfejs, którym takie informacje
nadzorca będzie mógł sobie od nich pobrać.
Sam musisz wybrać odpowiednie rozwiązanie.
> * Rozwiązanie proste
> Aplikacja, czyli wątek główny, zajmuje się pobieraniem danych z tablicy,
> uruchamianiem wątków oraz pilnowaniem żeby z ich liczbą nie przesadzić
> (max 8 np). Wątki mogą informować takiego nadzorcę, że już zakończyły
> rzeźbienie i się zamknęły tak, aby mógł on odpalić kolejny z nową porcją
> danych. Sam nadzorca może też trzymać wątki w innej tablicy, okresowo
> sprawdzać czy któryś z nich sie nie zakończył i uzupełniać braki do 8.
>
> * Rozwiązanie nieco bardziej złożone
> Nadzorca uruchamia 8 wątków (tzw. workerów), które są uruchomione przez
> cały czas. Następnie przekazuje im dane z tablicy do obróbki i
> opcjonalnie sprawdza czy którys z nich nie poszedł w krzaki (zdarza się)
> na co odpowiednio reaguje (zapis w logu, cofnięcie nieobrobionych danych
> do ponownego przetworzenia) i uzupełnia brak w wątkach. Każdy worker musi
> albo informować nadzorcę o tym, że zakończył miętosić dane i czeka na
> nową porcję, lub udostępniać jakiś interfejs, którym takie informacje
> nadzorca będzie mógł sobie od nich pobrać.
8 wątków-konsumentów i wspólna kolejka (Queue.Queue) z producentem na
drugim końcu. Wątek pobiera zadanie z kolejki, wykonuje je, kończy i
zgłasza się do kolejki po kolejne zadanie. I tak w kółko, dopóki
producent nie włoży do kolejki czegoś, co zasygnalizuje koniec danych.
Ale w przypadku działalności czysto obliczeniowej nic te wątki nie
dadzą, bo przyjdzie GIL i zrobi porządek.
--
Jarek Zgoda
http://zgodowie.org/
"We read Knuth so you don't have to" - Tim Peters
>> 8 wątków-konsumentów i wspólna kolejka (Queue.Queue) z producentem na
>> drugim końcu. Wątek pobiera zadanie z kolejki, wykonuje je, kończy i
>> zgłasza się do kolejki po kolejne zadanie. I tak w kółko, dopóki
>> producent nie włoży do kolejki czegoś, co zasygnalizuje koniec danych.
> Baaaardzo ryzykowne. Jeśli worker sie wykrzaczy, director nie bedzie
> wiedział jakie dane ten wątek obrabiał i nie będzie mógł ich wycofać do
> ponownego przeliczenia, czyli zostaną one stracone.
> Worker mógłby np. zapisywać w swoim atrybucie ostatnie dane jakie obracał i
> director po dostaniu z niego False na isAlive() dałby radę nieprzeliczone
> dane wyciągnąć i wrzucić spowrotem do kolejki po usunięciu martwego i
> odpaleniu brakującego workera. Myślę, że to by zadziałało. Jedna kolejka
> dla wszystkich = mniej zmartwień dla directora = mniej miejsc gdzie może
> wystąpić błąd.
Moim zdaniem przesadzasz, dane nie nadające się do obróbki i tak nie
mogłyby zostać przetworzone. Ale od nadmiaru ostrożności jeszcze nikt
nie umarł, co najwyżej napisał więcej kodu. ;)
> Ale od nadmiaru ostrożności jeszcze nikt nie umarł, co najwyżej napisał
> więcej kodu. ;)
Skomplikowane to nie jest, dużo dodatkowego kodu tez nie trzeba, a operator
nie będzie programisty wyzywał jeśli coś się nie powiedzie ;)
Oczywiście, możemy także uznać, że jeśli np. wyczerpały nam się zasoby, to
całość obliczeń trzeba zakończyć i coś zmienić (w danych, kodzie, czy
systemie). Co kto woli :)
pozdrawiam
p.s.
A ja jeszcze zamieszam :) Jeśli to ma chodzić na maszynie
wieloprocesorowej/wielordzeniowej, to może lepiej odpalić osobne
procesy, niż wątki?
--
pozdrawiam
Piotr Chamera
> a ja juz sie pogubilem, wiec moze zamiast poprzedniej wersji
> wywolac osiem watkow, i jak sie skoncza wszystkie to wywolac
> kolejne osiem i tak az do konca??
Możesz jeśli chcesz. Wybór należy do Ciebie. Zastosowanie jednej kolejki
Queue.Queue() jest IMHO najlepszym wyjściem. Pomocna tutaj jest
implementacja samej klasy Queue, która jest thread-safe co oznacza, że
ciężar projektowania reguł dostępu do niej został już zdjęty z Twoich
barków i nie musisz się zbytnio tym przejmować. Możesz zczytać, czy też
włożyć do niej dane w wątku bez sprawdzania czy aby inny wątek nie wpadł w
tym samym czasie na ten sam pomysł. Rozwiązanie to jest też dość proste do
realizacji.
Natomiast spawnowanie ośmiu wątków na raz, czekanie aż się zakończą i
uruchamianie kolejnych, tak jak proponujesz, jest też jakimś rozwiązaniem
lecz niezbyt wydajnym i niezupełnie tym, które proponowałem. Jeśli z tych
ośmiu wątków cztery zakończą obliczenia, to można przecież zastąpić je
nowymi aby sobie liczyły kolejne porcje danych, bez czekania na pozostałe
cztery. Jako pierwsze rozwiązanie proponowałem Ci uruchomienie ośmiu wątków
i stałe monitorowanie czy którys z nich się nie zakończył i jeśli tak jest,
zastąpienie go nowym. W ten sposób przez cały czas coś się liczy.
> bo sie juz zupelnie pogubilem czytajac wasze wypowiedzi .... :(
Masz tutaj dyskusję między kolesiem, który nawet "print 'Hello World'"
objąłby instrukcją try-except gdyby mógł (ja) oraz osoby, która podchodzi
do takich spraw bardziej trzeźwo (Jarek). Tylko Ty wiesz które podejście
jest odpowiednie dla Twojego przypadku. Być może żadne z nich i najlepiej
sprawdzi się średnia. Jarek jest też znacznie bardziej doświadczony ode
mnie w tych sprawach, więc jego pomysły mają wyższy priorytet przy
podejmowaniu decyzji "kogo sluchać" :)
Musisz się też liczyć z tym, że na grupach dyskusyjnych wątki (te usenetowe,
nie pythonowe) po kilku postach zaczynają odpływać od postawionego pytania
i rozmówcy zaczynają dyskutować/spierać się/obrzucać błotem/whateva na
tematy, ktore pytającego zbytnio nie interesują. Taki folklor grup
dyskusyjnych :)
tylko jakby to wygladalo z subprocesami, bo tej funkcji tez jesce nie
mialem
okazji poznac
pozdrawiam
p.s.
piasekps napisał(a):
Mysle, ze potrzebujesz czegos takiego:
http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/475160
Wypelnij odpowiednio `TaskQueue`, aby Twoj `worker` mogl pobrac
tyle danych ile mu potrzeba i zapusc na tym analogicznie pule watkow.
RW
Rob Wolfe napisał(a):
> piasekps napisa�(a):
> Mysle, ze potrzebujesz czegos takiego:
> http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/475160
>
> Wypelnij odpowiednio `TaskQueue`, aby Twoj `worker` mogl pobrac
> tyle danych ile mu potrzeba i zapusc na tym analogicznie pule watkow.
Nawiasem mowiac w Pythonie 2.5 mozna uzyc poprostu obiektu
Queue z biblioteki standardowej:
http://docs.python.org/lib/QueueObjects.html
RW
>> A ja jeszcze zamieszam :) Jeśli to ma chodzić na maszynie
>> wieloprocesorowej/wielordzeniowej, to może lepiej odpalić osobne
>> procesy, niż wątki?
> O ile się nie mylę, to np. na nowych kernelach linuksa wątki też są już
> rozrzucane po rdzeniach.
Wątki systemu operacyjnego to jedno, a GIL maszyny wirtualnej Pythona to
drugie. I GIL wygrywa, maszyna wirtualna w danej chwili może wykonywać
kod tylko jednego wątku. Dopóki działalność wątku nie powoduje
zwolnienia GIL (np. oczekiwanie na IO, wykonywanie kodu w odpowiednio
napisanym rozszerzeniu w C), niczego się nie zyskuje na uruchamianiu
kolejnych wątków, a wręcz wydajność traci przez narzut na tworzenie
wątków i przełączanie kontekstu wykonania.
W poszukiwaniach metod obejścia problemu GIL (ale tylko wtedy, gdy on
naprawdę występuje), polecam zapoznanie się ze stroną
http://wiki.python.org/moin/ParallelProcessing -- ja mogę polecić
ParallelPython (http://www.parallelpython.com/) i processing
(http://pypi.python.org/pypi/processing).
--
Jarek Zgoda
Skype: jzgoda | GTalk: zg...@jabber.aster.pl | voice: +48228430101
"We read Knuth so you don't have to." (Tim Peters)
Nawiasem mówiąc, można to zrobić także we wcześniejszych wersjach Pythona.
Jarek Zgoda napisał(a):
> Rob Wolfe napisa�(a):
>
> >> Mysle, ze potrzebujesz czegos takiego:
> >> http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/475160
> >>
> >> Wypelnij odpowiednio `TaskQueue`, aby Twoj `worker` mogl pobrac
> >> tyle danych ile mu potrzeba i zapusc na tym analogicznie pule watkow.
> >
> > Nawiasem mowiac w Pythonie 2.5 mozna uzyc poprostu obiektu
> > Queue z biblioteki standardowej:
> > http://docs.python.org/lib/QueueObjects.html
>
> Nawiasem mowiac, mozna to zrobic takze we wczesniejszych wersjach Pythona.
Wszystko mozna zrobic.
Mialem na mysli metody `join` i `task_done` dodane w 2.5, ktore sprawe
ulatwiaja.
RW
a propo subprocesow
czy mozna jako subproces odpalic jakas wlasna definicje
czy musi ona byc zapisana jako osobny plik.py i tak
dopiero ja odpalic? jesli tak to czy mozna przekazac jakos temu
skyrptowi zmienne? czyli odpalam skrypt A generuje jakies w nim
zmienne
i odpalam skrypt B z jakimis zmiennymi pobranymi ze skryptu A
pozdrawiam
p.s.
W ramach samokształcenia, jako początkujący pythonauta, spłodziłem coś
takiego (kod poniżej). Mam nadzieję, że znajdą się osoby bardziej
doświadczone, które to skomentują, napiszą co można (lub należy) zmienić
aby napisać to bezpiecznie i bardziej pythonicznie :)
<code>
# -*- coding: utf-8 -*-
import time
import subprocess
# przykładowa kolejka zadań
zadania = [
('gzip', 'infile1'),
('gzip', 'infile2'),
('gzip', 'infile3'),
('gzip', 'infile4'),
('gzip', 'infile5'),
('gzip', 'infile6'),
('gzip', 'infile7'),
('gzip', 'infile8'),
]
liczba_jednoczesnych_zadan = 3
def wykonaj(zadania):
pula = []
while True:
for proces in pula: # sprawdź czy jakieś zadania się zakończyły
if proces.poll() is not None:
# tu należałoby sprawdzić co się stało z procesem
# (process.returncode) i ewentualnie
# jakoś zadziałać, np. zwrócić zadanie do kolejki
# w przypadku niepowodzenia
print 'koniec: ' + proces.__repr__()
pula.remove(proces)
while len(pula)<liczba_jednoczesnych_zadan:
# w miejsce zakończonych zadań uruchom następne
if len(zadania)>0 :
zadanie = zadania.pop()
proces = subprocess.Popen(zadanie)
# tu może należałoby sprawdzić czy uruchomienie
# procesu się powiodło
pula.append(proces)
print 'start: ' + proces.__repr__()
else:
# jeśli nie ma już nowych zadań,
# sprawdź czy wszystkie już się zakończyły
if len(pula)==0:
# jeśli nie ma działających zadań zakończ
return
else:
# jeśli są wróć do pętli
break
# odczekaj pół sekundy i sprawdź ponownie
time.sleep(0.5)
wykonaj(zadania)
</code>
Jeśli parametr jest jakimś prostym typem, to najłatwiej chyba jest
przekazać go przez linie poleceń (popen przyjmuje listę, pierwszy jest
program do uruchomienia a reszta to parametry) i pobrać go w skrypcie B
z sys.argv
while 1:
for proces in pula:
if proces.poll() is not None:
pula.remove(proces)
if zadania:
pula.append(subprocess.Popen(zadania.pop()))
if not pula and not zadania:
break
time.sleep(0.5)
O ile uzywasz standardowego CPythona.
Stackless Python, IronPython ani Jython GIL-a nie maja.
Na starcie pula jest pusta :) więc śpimy,
i budzimy się co 0.5 s żeby zobaczyć, że nadal jest pusta :(
Ta wersja wymagałaby wstępnego wypełnienia puli.
W mojej wersji pętla 1 czyści pulę, pętla 2 uzupełnia brakujące zadania.
Na starcie 1 zostanie pominięta i przechodzimy do 2.
--
pozdrawiam
Piotr Chamera
te procesy trzeba rozpoczac przed petla while?
bo w przykladzie z 2 petlami Piotra, to bylo jasne jak dziala
a w tym jak wywoluje funkcje to, tak jak Piotr mowi caly czas
czeka i czeka
Równoważna funkcja w wersji Daniela wyglądałaby chyba tak:
<code>
def wykonaj(zadania, liczba_jednoczesnych_zadan = 3):
pula = []
for i in range(liczba_jednoczesnych_zadan):
if zadania:
proces = subprocess.Popen(zadania.pop())
pula.append(proces)
print 'start: ' + proces.__repr__()
while 1:
for proces in pula:
if proces.poll() is not None:
pula.remove(proces)
print 'koniec: ' + proces.__repr__()
if zadania:
proces = subprocess.Popen(zadania.pop())
pula.append(proces)
print 'start: ' + proces.__repr__()
if not pula and not zadania:
break
time.sleep(0.5)
</code>
wadą jest konieczność dwukrotnego zakodowania dotyczącego odpalania
procesu. W wersji bez drukowania są to tylko dwie linie, ale
if zadania:
pula.append(subprocess.Popen(zadania.pop()))
ale zawsze trzeba to napisać dwa razy.
Wymyśliłem jeszcze jedną wersję z nazwałbym to "miękkim startem".
Jest krótsza i prostsza od poprzednich ale startuje po jednym
procesie w każdym cyklu oczekiwania. Odstęp między zakończeniem procesu
a odpaleniem nowego też jest równy temu czasowi. Przy długo działających
procesach prawdopodobnie nie będzie to miało znaczenia.
<code>
def wykonaj(zadania, liczba_jednoczesnych_zadan = 3):
pula = []
while 1:
if zadania and len(pula)<liczba_jednoczesnych_zadan:
pula.append(subprocess.Popen(zadania.pop()))
for proces in pula:
if proces.poll() is not None:
pula.remove(proces)
if not pula:
break
time.sleep(0.5)
</code>
--
pozdrawiam
Piotr Chamera
> wadą jest konieczność dwukrotnego zakodowania dotyczącego odpalania
> procesu. W wersji bez drukowania są to tylko dwie linie, ale
>
> if zadania:
> pula.append(subprocess.Popen(zadania.pop()))
>
> ale zawsze trzeba to napisać dwa razy.
Jeśli jest to bardzo rażące, to można nieco zakombinować:
class Kindergarden(object):
def __init___(self, zadania, pojemnosc=3):
self.zadania = zadania
self.pojemnosc = pojemnosc
self.dzieci = []
def dodaj_dziecko(self):
if self.zadania:
self.dzieci.append(subprocess.Popen(self.zadania.pop()))
def usun_dziecko(self, dziecko):
self.dzieci.remove(dziecko)
self.dodaj_dziecko()
def zapelnij_dziecmi(self):
for i in xrange(self.pojemnosc):
self.dodaj_dziecko()
def usun_martwe_dzieci(self):
for dziecko in self.dzieci:
if dziecko.poll() is not None:
self.usun_dziecko(dziecko)
self.dodaj_dziecko()
return self.dzieci
kindergarden = Kindergarden(lista_zadan, ilosc_dzieci)
kindergarden.zapelnij_dziecmi()
dzieci = kindergarden.dzieci # Dlaczego Python nie ma pętli do-while?
while dzieci:
dzieci = kindergarden.usun_martwe_dzieci()
time.sleep(0.5)
Wydaje mi się jednak, że to troche przekombinowane ;)
> Wymyśliłem jeszcze jedną wersję z nazwałbym to "miękkim startem".
Tez dobre :D
dziala tak jak chcialem, a jeszcze jedno pytanie, czy program B teraz
moze przeslac
jakies wartosci do programu A (program A caly czas dziala)
dokladniej chodzi mi o cos takiego aby A uruchamial program B dawal mu
zmienne
od 1-8, i jesli sie jakis B zakonczy zeby przeslal nr spowrotem do A,
aby kolejny program
zostal uruchomiony w to zwolnione miejsce, (uruchamiam najpierw od B1-
B8,
powiedzmy konczy prace B6 zwraca liczbe 6 a ja B9 uruchamiam
jako B6)
pewnym rozwiazaniem jest zapisywanie do pliku jak zakonczy dzialanie i
sprawdzanie
w petli rozpoczynajacej sie czy cos sie zwolnilo, ale moze jest inny
sposob
dzieki
pozdrawiam
p.s.
Jeśli przyjrzysz się rozwiązaniom, które podałem w jednym z wczorajszych
postów albo tym podanym przez Daniela, to one wszystkie odpowiadają
programowi A z twojego przykładu. Dla zadanej, dowolnie długiej ( :) )
kolejki programów B do wykonania uruchamiają zadana ilość programów B
i monitorują ich zakończenie, zastępując zakończone nowymi z kolejki, aż
do wyczerpania wszystkich zadań.
Jeśli A wie, który numer programu B uruchomił, to może sobie to zapisać
(np. w zmiennej pula, w cytowanych przykładach) i jeśli któryś B się
zakończy A może sobie odczytać jego numer. B może tylko zwrócic kod
powrotu (returncode) zeby było wiadomo czy zakończył się OK czy z błędem.
A jeśli potrzebna jest jednak komunikacja, to tak jak przed chwila
Daniel napisał.
i = 0
while True:
for proces in pula:
if proces.poll() is not None:
i += 1
print str(proces.pid) + " - %s" % i
pula.remove(proces)
if len(zadania) > 0 :
wr = subprocess.Popen(zadania.pop())
j += 1
wr = subprocess.Popen(zadania.pop())
print "%s -- %s" % (j, wr.pid)
pula.append(wr)
if not pula and not zadania:
break
time.sleep(0.5)
wykonaj(zadania, liczba_zadan)
mam ten wasz kod, wyswietlam za pomoca 'pid' id procesu, w tablicy
zadania mam tak zdefiniowane:
for kkkk in range(20):
zadania.append(('python', '/Users/MAC010/Slawek/trtr.py', '%s' %
kkkk) )
plik trtr.py narazie kaze mu tylko wyswietlac zmienna kkkk
moje pytanie dlaczego nie wyswietlaja mi sie kkkk na konsoli
i drugie pytanie, dlaczego mimo 20 zadan w tablcy 'zadania'
wykonuje sie tylko 13 procesow,
if proces.poll() is not None:
i += 1
print str(proces.pid) + " - %s" % i
tu kaze wyswietlac id i kolejny nr i i program zatrzymuje sie na 13
pozdrawiam
p.s.
> def wykonaj(zadania, liczba):
> j = 0
Po co Ci ten licznik?
> pula = []
> for step in range(liczba):
> if zadania:
> j += 1
> wr = subprocess.Popen(zadania.pop())
> pula.append(wr)
> print "%s -- %s" % (j, wr.pid)
Zamiast "j" użyj len(pula)
> i = 0
>
> while True:
> for proces in pula:
>
> if proces.poll() is not None:
> i += 1
> print str(proces.pid) + " - %s" % i
> pula.remove(proces)
> if len(zadania) > 0 :
> wr = subprocess.Popen(zadania.pop())
> j += 1
> wr = subprocess.Popen(zadania.pop())
Po co podwójne wywołanie subprocess.Popen()?
> print "%s -- %s" % (j, wr.pid)
Uwaga co do "j" jak wyżej.
> mam ten wasz kod, wyswietlam za pomoca 'pid' id procesu, w tablicy
> zadania mam tak zdefiniowane:
>
> for kkkk in range(20):
> zadania.append(('python', '/Users/MAC010/Slawek/trtr.py', '%s' %
> kkkk) )
Jeśli odpalasz skrypt pythonowy, który sam piszesz, to może ten kod wrzucić
do głównego skryptu i uruchamiać przez os.fork()? Nie będzie znacznie
wygodniej?
> plik trtr.py narazie kaze mu tylko wyswietlac zmienna kkkk
> moje pytanie dlaczego nie wyswietlaja mi sie kkkk na konsoli
Czy-tać do-ku-men-ta-cję!
Z opisu konstruktora klasy subprocess.Popen:
"""
stdin, stdout and stderr specify the executed programs' standard input,
standard output and standard error file handles, respectively. Valid values
are PIPE, an existing file descriptor (a positive integer), an existing
file object, and None. PIPE indicates that a new pipe to the child should
be created. With None, no redirection will occur; the child's file handles
will be inherited from the parent. Additionally, stderr can be STDOUT,
which indicates that the stderr data from the applications should be
captured into the same file handle as for stdout.
"""
Jako "file handles" użyj sys.stdout i/lub sys.stderr. IMHO jednak to kiepski
pomysł przy takiej ilości podprocesów.
> i drugie pytanie, dlaczego mimo 20 zadan w tablcy 'zadania'
> wykonuje sie tylko 13 procesow,
>
> if proces.poll() is not None:
> i += 1
> print str(proces.pid) + " - %s" % i
> tu kaze wyswietlac id i kolejny nr i i program zatrzymuje sie na 13
Bo tu wyświetlasz tylko te procesy, które jeszcze działają, a olewasz te,
które się zakończyły i czekają na czyszczenie. Sprawdź raczej zawartość
tablicy "pula".
O tym już Daniel napisał
> i drugie pytanie, dlaczego mimo 20 zadan w tablcy 'zadania'
> wykonuje sie tylko 13 procesow,
>
> if proces.poll() is not None:
> i += 1
> print str(proces.pid) + " - %s" % i
> tu kaze wyswietlac id i kolejny nr i i program zatrzymuje sie na 13
>
Tu wrócę jeszcze raz do swojego kodu i lekko zmodyfikuję żeby drukował
Twoje numerki :) Poczytaj uważnie komentarze w poniższym kodzie i
zastanów się jak to działa.
# -*- coding: utf-8 -*-
import time
import subprocess
# tu Twój kod do tworzenia listy zadań
# dałem tu słownik i zapisuję w nim numerek i zadanie
# ale wcale nie musi to tak wyglądać
zadania = []
for kkkk in range(20):
zadania.append({
'numer': kkkk,
'zadanie': ('python', '/Users/MAC010/Slawek/trtr.py', '%s' %
kkkk)})
def wykonaj(zadania, liczba_jednoczesnych_zadan = 5):
pula = []
while True:
for proces in pula: # sprawdź czy jakieś zadania się zakończyły
if proces.poll() is not None:
# tu należałoby sprawdzić co się stało z procesem
# (process.returncode) i ewentualnie
# jakoś zadziałać, np. zwrócić zadanie do kolejki
# w przypadku niepowodzenia
# tu wyświetlasz dane o zakończonym procesie
print 'koniec: ' + proces.moje_dane['numer']
pula.remove(proces)
while len(pula)<liczba_jednoczesnych_zadan:
# w miejsce zakończonych zadań uruchom następne
if len(zadania)>0 :
zadanie = zadania.pop()
proces = subprocess.Popen(zadanie['zadanie'])
# tu może należałoby sprawdzić czy uruchomienie
# procesu się powiodło
# tu możesz przyczepić dowolne dane o uruchamianym
# procesie (numer, parametry, itp.) ja zapisuję po
# prostu słownik z numerem i uruchomionym poleceniem
proces.moje_dane = zadanie
pula.append(proces)
# tu wyświetlasz dane o startowanym procesie
print 'start: ' + proces.moje_dane['numer']
else:
# jeśli nie ma już nowych zadań,
# sprawdź czy wszystkie już się zakończyły
if len(pula)==0:
# jeśli nie ma działających zadań zakończ
return
else:
# jeśli są wróć do pętli
break
# odczekaj pół sekundy i sprawdź ponownie
time.sleep(0.5)
wykonaj(zadania, 5)
Jeśli uruchomiony proces działa krótko, może Cię nieco zaskoczyć
kolejność odpalania i kończenia procesów. Jeśli odpalane procesy nigdy
się nie kończą, program odpali tylko 5 sztuk i zawiśnie :)
>Jeśli odpalasz skrypt pythonowy, który sam piszesz, to może ten kod wrzucić
>do głównego skryptu i uruchamiać przez os.fork()? Nie będzie znacznie
>wygodniej?
no nie wiem wlasnie, zabawe z pythonem zaczolem jakies 3-4 miesiace
temu i jesce nie poznalem wszystkich modulow, class etc.
dzieki za szybka pomoc
pozdrawiam
p.s.
pisalem 'proste' programy matematyczne, gdzie nie bylo potrzebne
uruchamiac
kilku procesow naraz ;), same obliczenia, i nie wazne, ze czasem
program
lecial caly dzien, byle wyniki byly prawidlowe :P
pozdrawiam
p.s.
> Aha, no i klasyczna pipa, czyli gadanie przez stdin/stdout, ale to potrafi
> być upierdliwe. Upierdliwość można zmniejszyć używając select.select() lub
> select.poll()
W tym artykule jest to fajnie pokazane (punkt A Multi-Processor Python
Solution):
http://effbot.org/zone/wide-finder.htm (wiem, że już było, ale podane w
innym kontekście).
--
MAcks
Skończyło się na użyciu wynalazku pt. Pyro. Jest podobnych modułów więcej, a
ten jest chyba najprostszym z dostępnych. Krótko mówiąc, służy do pisania
programów rozproszonych (coś, a'la Cobra dla ubogich). Klasy Pythona
komunikują się ze sobą, nie zważając na to, że wykonują je interpretery
działające na innych komputerach. Skoro dwa Pythony na różnych maszynach mogą
ze sobą rozmawiać, to mogą również rozmawiać działając na jednym komputerze...
do takiego wniosku doszedłem.
Nie jestem świadom słabych stron takiego rozwiązania. Może ktoś takie widzi i
je tu opisze. Dla mojego programu Pyro sprawdził się. W sensie ilości linijek
kodu i prostoty jest to rozwiązanie idealne. Czas przekazywania obiektów
między procesami mnie nie robił kłopotów. Nie sądzę, żeby było to wolniejsze
od pipes, tym bardziej że możemy przekazywać nie tylko parametry, ale dowolne
hashowalne obiekty pythona.
Cały urok tego narzędzia polega na tym, że nie trzeba się w tym przypadku
bawić w żadne protokoły a mimo to dostajemy za free komunikacje dwukierunkową
między procesami. Dla programu serwera, każdy klient to po prostu klasa, którą
się woła i zapisuje to, co zwraca. To bardzo wygodne, szczególnie dla
początkujących i niedzielnych pythonistów - takich jak ja.
Mam nadzieję, że pomoże:
http://pyro.sourceforge.net/
pozdrawiam,
skk.
--
Wysłano z serwisu Usenet w portalu Gazeta.pl -> http://www.gazeta.pl/usenet/
> Cały urok tego narzędzia polega na tym, że nie trzeba się w tym przypadku
> bawić w żadne protokoły a mimo to dostajemy za free komunikacje
> dwukierunkową między procesami. Dla programu serwera, każdy klient to po
> prostu klasa, którą się woła i zapisuje to, co zwraca. To bardzo wygodne,
> szczególnie dla początkujących i niedzielnych pythonistów - takich jak ja.
A szkoda, gdyż programowanie komunikacji sieciowej jest bardzo ciekawym
tematem i nie tak trudnym jak się może wydawać.
1) Mało rozszerzalne rozwiązanie? Owszem, ale jeśli ktoś piszę tylko w
Pythonie, nie ma problemu.
2) Warstwa sieciowa oczywiście musi być obecna, ale w 99% jest obecna,
przecież nie mówimy o programowaniu Bóg wie jakiego urządzenia, tylko PC pod
kontrolą Linuxa, OXS lub WinXP. Szczerze mówiąc w przypadku programów, którymi
ja się zajmuje, żaden nie będzie działać bez warstwy sieciowej, mimo że
"oficjalnie" nie ma z siecią nic wspólnego (grafika komputerowa), bo
poszczególne komponenty wielu programów komunikują się za pomocą TCP.
3) Zanim spróbowałem Pyro, przyjrzałem się tematowi RPC i z punktu widzenia
kogoś niezbyt biegłego, różnica jest zasadnicza. Jeśli ktoś pracuje nad dużym,
przenośnym, profesjonalnym narzędziem, owszem, pewnie wypadałoby zaprojektować
jakiś protokół w oparciu o rozwiązania do tego stworzone, ale skoro ktoś nie
ma to to czasu, takie Pyro pomaga bardzo.
4)Jest przy tym znacznie przyjaźniejsze w użyciu niż subprocess, któremu, jak
wiesz, możesz przekazać parametry, a nie obiekty, i dla którego
zaprojektowanie komunikacji dwukierunkowej wymaga nie lada gimnastyki z
plikami pośrednimi itp. Właśnie o tej wygodzie pisałem.
5) Pyro, jak pewnie każde rozwiązanie tego typu, ma kontrolę nad tym, które
podsystemy są dostępne, a które nie i wolałbym napisać kilka linijek, które to
sprawdzają, niż bawić się w picklowanie obiektów pythona, żeby je przekazać do
subprocessu i z powrotem, co jest chyba bardziej "error prone", niż dobrze
zaprojektowany przez fachowców system rozproszony, który jest równie łatwy w
obsłudze, co standardowy subprocess.
Powtórzę, że mówię, o potrzebie wykonania możliwe małym kosztem systemu
paralelnego czy wieloprocesorowego, a nie użyciu Pyro do zaprojektowania
"fully featured parallel computing system", bo o tym nic mi nie wiadomo.
pozdr.,
> 1) Mało rozszerzalne rozwiązanie? Owszem, ale jeśli ktoś piszę tylko w
> Pythonie, nie ma problemu.
Ałaaa... To jest bardzo częste rozumowanie, które nieco mniej często okazuje
się błędne. Nigdy nie wiesz czy ten mały projekcik w Pythonie nie rozrośnie
się w przyszłości, lub nie okaże się, że bardzo fajnie byłoby połączyć go z
innym systemem opartym na innej platformie. Wtedy może być konieczne
przepisanie połowy aplikacji. Dlatego ważne jest, aby nawet w małych
projektach stosować standardy tak bardzo jak to możliwe, aby ewentualne
późniejsze skalowanie było "bezbolesne". Jeśli na tym komuś nie zależy, to
zawsze jest jeszcze satysfakcja z oglądania min analityków, czy innych
wdrożeniowców, kiedy mówisz "Tak, oczywiście że nie ma problemu z
interoperacyjnością. Nasz system posiada interfejs XMLRPC. Oto
specyfikacja" :D
> 2) Warstwa sieciowa oczywiście musi być obecna, ale w 99% jest obecna,
> przecież nie mówimy o programowaniu Bóg wie jakiego urządzenia, tylko PC
> pod kontrolą Linuxa, OXS lub WinXP.
Linuksa powiadasz... "ip link set dev lo down" i nie ma loopback ;)
No, i pojawiają się pytania userów typu "co? Edytor tekstu, ktory potrzebuje
127.0.0.1? Co to jest?" ;)
> Szczerze mówiąc w przypadku programów,
> którymi ja się zajmuje, żaden nie będzie działać bez warstwy sieciowej,
> mimo że "oficjalnie" nie ma z siecią nic wspólnego (grafika komputerowa),
> bo poszczególne komponenty wielu programów komunikują się za pomocą TCP.
Mhm. Ale Twoje środowisko nie musi być takie samo jak środowisko innego
programisty. Nie zrozum mnie źle, nie krytykuję Twojego pomysłu. Pyro to
bardzo dobre rozwiązanie. Ja zwracam tylko uwagę na rzeczy, o których
trzeba pamiętać przy jego wykorzystywaniu i jakie są wymagania wobec
systemu, na którym dana aplikacja będzie działać.
> 3) Zanim spróbowałem Pyro, przyjrzałem się tematowi RPC i z punktu
> widzenia kogoś niezbyt biegłego, różnica jest zasadnicza. Jeśli ktoś
> pracuje nad dużym, przenośnym, profesjonalnym narzędziem, owszem, pewnie
> wypadałoby zaprojektować jakiś protokół w oparciu o rozwiązania do tego
> stworzone, ale skoro ktoś nie ma to to czasu, takie Pyro pomaga bardzo.
Nie musisz projektować żadnego protokołu. XMLRPC jest już zaprojektowany od
dawna i nie musisz wymyślać koła na nowo. Zaimplementowanie takiego
interfejsu jest banalnie proste i sprowadza się do obudowania wszystkich
funkcji jedną klasą po stronie serwera i (teoretycznie) jedną linijką kodu
po stronie klienta. Nawet nie trzeba być w tym biegłym, gdyż moduł
xmlrpclib przychodzący z Pythonem zgrabnie ukrywa przed programistą całe to
wożenie się z protokołem HTTP. Jedyna wiedza jaka byłaby wskazana, to praca
z wątkami.
> 4)Jest przy tym znacznie przyjaźniejsze w użyciu niż subprocess, któremu,
> jak wiesz, możesz przekazać parametry, a nie obiekty, i dla którego
> zaprojektowanie komunikacji dwukierunkowej wymaga nie lada gimnastyki z
> plikami pośrednimi itp. Właśnie o tej wygodzie pisałem.
Sama zamiana obiektu w string jest prosciutka i nawet masz do wyboru kilka
różnych rozwiązań, od prostych (serializacja, np. picklem) po złożone
(XML). Nie musisz się bawić w pliki pośrednie, gdyż wystarczy
stdin/stdout/stderr. Całe to Pyro wygląda na połączenie jakiegoś protokołu
sieciowego z serializacją. Jest jednak o tyle prostsze, że ktoś to już
napisal za nas :)
> 5) Pyro, jak pewnie każde rozwiązanie tego typu, ma kontrolę nad tym,
> które podsystemy są dostępne, a które nie i wolałbym napisać kilka
> linijek, które to sprawdzają, niż bawić się w picklowanie obiektów
> pythona, żeby je przekazać do subprocessu i z powrotem, co jest chyba
> bardziej "error prone", niż dobrze zaprojektowany przez fachowców system
> rozproszony, który jest równie łatwy w obsłudze, co standardowy
> subprocess.
Jeśli stawiamy go naprzeciw subprocess, to tak. Jest lepszym rozwiązaniem
niż pipe.
> Powtórzę, że mówię, o potrzebie wykonania możliwe małym kosztem systemu
> paralelnego czy wieloprocesorowego, a nie użyciu Pyro do zaprojektowania
> "fully featured parallel computing system", bo o tym nic mi nie wiadomo.
Rozwiązanie dobre jak każde inne, czyli ma swoje plusy i minusy. Do
projektanta należy wybór właściwego sposobu komunikacji, zależnego od
projektu.