Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

watki-thread opis

109 views
Skip to first unread message

piasekps

unread,
Feb 5, 2008, 1:31:15 PM2/5/08
to
Czesc Wszystkim

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.

Daniel Mróz

unread,
Feb 5, 2008, 1:43:38 PM2/5/08
to
piasekps wrote:
> Czy macie jakies linki do dobrych opisow gdzie bedzie w sposob jasny
> (jak dla 'debila')
> wytlumaczony jak dzialaja 'watki' w pythonie?
A z czym konkretnie masz problem?
Wątki są bardzo proste. Tworzysz sobie klasę dziedziczącą po
threading.Thread i pokrywasz metodę run() tym, co ma ten wątek robić. Potem
w głównym wątku tworzysz instancję tej klasy i wywołujesz jej metodę
start() uprzednio ustawiając setDaemon() wedle uznania (GOTO dokumentacja).
Bardzo przydatne w komunikacji między wątkami jest też korzystanie z
threading.Lock() i threading.Event() (pamiętaj, że nie możesz zabić wątku
tylko go poprosić żeby się łaskawie zakończył oraz że operowanie na
obiektach współdzielonych bez lockowania może się skończyć nieciekawie).
Prosty przykład:

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+ ]

Daniel Mróz

unread,
Feb 5, 2008, 1:46:15 PM2/5/08
to
Daniel Mróz wrote:
> thread1 = MyThread()
> thread1.run()
>
> thread2 = MyThread()
> thread2.run()
>
> thread3 = MyThread()
> thread3.run()
Przepraszam. Łyknąłem sobie kawy i zauważyłem babola. Zamiast thread?.run()
powinno być thread?.start().

piasekps

unread,
Feb 5, 2008, 2:06:19 PM2/5/08
to
chcialbym zrobic cos takiego ze mam w tablicy jakies wartosci (jest
ich okolo 50)
chcialbym pobierac z tej tablicy 8 wartosci i odpalic jakas funckje z
tymi wartosciami
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

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

Daniel Mróz

unread,
Feb 5, 2008, 2:30:36 PM2/5/08
to
piasekps wrote:
> chcialbym zrobic cos takiego ze mam w tablicy jakies wartosci (jest
> ich okolo 50)
> chcialbym pobierac z tej tablicy 8 wartosci i odpalic jakas funckje z
> tymi wartosciami
Odpalasz wątki z 8 wartościami pobranymi z tablicy...

> 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.

Jarek Zgoda

unread,
Feb 5, 2008, 3:13:08 PM2/5/08
to
Daniel Mróz pisze:

> * 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

Daniel Mróz

unread,
Feb 5, 2008, 3:24:03 PM2/5/08
to
Jarek Zgoda wrote:
> 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.

Daniel Mróz

unread,
Feb 5, 2008, 3:28:13 PM2/5/08
to
Daniel Mróz wrote:
>> 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.
Aha, jeszcze małe zabezpieczenie: jeśli ten sam zestaw danych zabija nam
workerów np. 3 razy, to usuwamy je z puli danych do przeliczenia ze
stosowną informacją dla administratora w logach tak, aby nie mielił
trującymi danymi w nieskończoność.

Jarek Zgoda

unread,
Feb 5, 2008, 4:07:00 PM2/5/08
to
Daniel Mróz pisze:

>> 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. ;)

Daniel Mróz

unread,
Feb 5, 2008, 4:25:07 PM2/5/08
to
Jarek Zgoda wrote:
>> 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.
Masz rację, lecz niemozność obrobienia danego zestawu danych nie musi
wynikać z tego, że są one nieobliczalne. Przy dużej ilości wątków
działających długo wykrzaczenie może być spowodowane np. wyczerpaniem
zasobów i wrzucenie takiego zestawu danych do ponownego przeliczenia jest
sensowne, gdyż przy następnej próbie wątków liczących może być mniej, lub
mogą wykonywać mniej zasobożerne obliczenia i próba taka się uda.
W przypadku obliczeń, które sumarycznie trwają kilka godzin, nieobrobienie
jednego zestawu poprawnych danych może sie wiązać z koniecznością
uruchomienia wszystkiego na nowo, czyli kupa czasu w plecy.
Przy zabezpieczeniu proponowanym przeze mnie w jednym z poprzednich postów,
nieobliczalne dane i tak wylecą.
Oczywiście wszystko można skrócić jeśli kod będzie pewien, że konkretnie ta
kombinacja danych jest nieobliczalna, ale wtedy raczej nie nastąpi
wykrzaczenie wątku, lecz łagodne zakończenie aplikacji o ile programista
taką możliwość przewidział.

> 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 :)

piasekps

unread,
Feb 5, 2008, 5:08:29 PM2/5/08
to
Daniel a mowiles ze watki sa proste :P
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?? bo sie juz zupelnie pogubilem
czytajac wasze wypowiedzi .... :(

pozdrawiam
p.s.

Piotr Chamera

unread,
Feb 5, 2008, 5:56:14 PM2/5/08
to

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

Daniel Mróz

unread,
Feb 5, 2008, 8:41:02 PM2/5/08
to
piasekps wrote:
> Daniel a mowiles ze watki sa proste :P
Bo same wątki są banalnie proste i sprowadzają się do przytoczonego
wcześniej przykładu :D
Największe komplikacje wprowadza komunikacja międzyprocesowa. W przypadku
wątków obiekty są współdzielone i operowanie na nich przez kilka różnych
instancji kodu wymaga stosowania się do reguł dostępu wspieranych lockami.
To potrafi być prawdziwą zmorą, gdyż źle zaprojektowane reguły mogą
spowodować zakleszczenie (np. conajmniej dwa wątki blokujące siebie
nawzajem i czekające aż "ten drugi" zwolni blokadę). Do tego wystarczy
nawet niewielki błąd czy zaniedbanie przy analizie działania algorytmów i
przepływu danych. To jest już trudne i wymaga uważnego projektowania.

> 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 :)

Daniel Mróz

unread,
Feb 5, 2008, 8:42:20 PM2/5/08
to
Piotr Chamera wrote:
> 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.

piasekps

unread,
Feb 6, 2008, 1:56:06 AM2/6/08
to
czyli co mowicie ze lepiej jest skorzystac z subprocess() zamiast tych
watkow?
a docelowo program ma dzialac na windzie ( :( ), wolalbym na linuxie,
ale nie
moge znalesc niczego jak skonvertowac plik .mov albo .avi do wmv w
rozdzielczosci HD
wiec zostaje windows z windows media encoder (chyba tak to sie nazywa)

tylko jakby to wygladalo z subprocesami, bo tej funkcji tez jesce nie
mialem
okazji poznac

pozdrawiam
p.s.

Rob Wolfe

unread,
Feb 6, 2008, 2:48:14 AM2/6/08
to

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

unread,
Feb 6, 2008, 2:54:15 AM2/6/08
to

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

Jarek Zgoda

unread,
Feb 6, 2008, 3:48:11 AM2/6/08
to
Daniel Mróz napisał(a):

>> 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)

Jarek Zgoda

unread,
Feb 6, 2008, 3:48:57 AM2/6/08
to
Rob Wolfe napisał(a):

Nawiasem mówiąc, można to zrobić także we wcześniejszych wersjach Pythona.

Rob Wolfe

unread,
Feb 6, 2008, 4:18:18 AM2/6/08
to

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

piasekps

unread,
Feb 6, 2008, 6:59:47 AM2/6/08
to

>
> 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 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.

Piotr Chamera

unread,
Feb 6, 2008, 7:02:12 AM2/6/08
to
piasekps napisał(a):

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>

Piotr Chamera

unread,
Feb 6, 2008, 7:32:23 AM2/6/08
to
piasekps napisał(a):

> 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

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

Daniel Mróz

unread,
Feb 6, 2008, 10:07:11 AM2/6/08
to
Piotr Chamera wrote:
> 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 :)
[...]
Po co Ci dwie pętle?

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)

Daniel Mróz

unread,
Feb 6, 2008, 10:09:02 AM2/6/08
to
Daniel Mróz wrote:
> 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)
Za bardzo się wcięło:

Bart Ogryczak

unread,
Feb 6, 2008, 12:15:27 PM2/6/08
to
On Feb 6, 9:48 am, Jarek Zgoda <jzg...@o2.usun.pl> wrote:
> > 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.

O ile uzywasz standardowego CPythona.
Stackless Python, IronPython ani Jython GIL-a nie maja.

Piotr Chamera

unread,
Feb 6, 2008, 1:24:58 PM2/6/08
to
Daniel Mróz napisał(a):

> 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)

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

Daniel Mróz

unread,
Feb 6, 2008, 1:42:59 PM2/6/08
to
Piotr Chamera wrote:
>> 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)
> 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.
Mhm. Startujesz tyle suprocessów ile chcesz mieć i potem one "pilnują się
same". Jedna pętla mniej :)

Slawomir Piasecki

unread,
Feb 6, 2008, 2:47:40 PM2/6/08
to
On 6 Lut, 19:42, Daniel Mróz <be...@alpha.pl> wrote:
> Piotr Chamera wrote:
> >> 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)
> > 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.
>
> Mhm. Startujesz tyle suprocessów ile chcesz mieć i potem one "pilnują się
> same". Jedna pętla mniej :)

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

Piotr Chamera

unread,
Feb 6, 2008, 3:54:26 PM2/6/08
to
Slawomir Piasecki napisał(a):

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

Daniel Mróz

unread,
Feb 6, 2008, 4:28:08 PM2/6/08
to
Slawomir Piasecki wrote:
>> Mhm. Startujesz tyle suprocessów ile chcesz mieć i potem one "pilnują się
>> same". Jedna pętla mniej :)
> te procesy trzeba rozpoczac przed petla while?
Tak

Daniel Mróz

unread,
Feb 6, 2008, 4:48:07 PM2/6/08
to
Piotr Chamera wrote:
>> te procesy trzeba rozpoczac przed petla while?
> Równoważna funkcja w wersji Daniela wyglądałaby chyba tak:
[CIAP... kod]
Dokładnie

> 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

Daniel Mróz

unread,
Feb 6, 2008, 4:49:44 PM2/6/08
to
Daniel Mróz wrote:
> def usun_dziecko(self, dziecko):
> self.dzieci.remove(dziecko)
> self.dodaj_dziecko()
Eeeeee.... self.dodaj_dziecko() jest tutaj nadmiarowe...

Slawomir Piasecki

unread,
Feb 7, 2008, 10:30:24 AM2/7/08
to
>
> 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

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.

Daniel Mróz

unread,
Feb 7, 2008, 11:07:52 AM2/7/08
to
Slawomir Piasecki wrote:
> dziala tak jak chcialem, a jeszcze jedno pytanie, czy program B teraz
> moze przeslac
> jakies wartosci do programu A (program A caly czas dziala)
Klasyczna pipa w tym przypadku powinna wystarczyć. Aplikacja B zwraca swoje
wyniki na stdout (w jakimś dowolnym formacie), a aplikacja A je sobie
zczytuje i obrabia wedle uznania.
Jeśli chcesz mieć komunikację dwukierunkową w trakcie działania obu
aplikacji, musisz się pobawić gniazdkami (uniksowymi, UDP lub TCP),
semaforami, plikami wymiany, nazwanymi kolejkami FIFO lub innym
wynalazkiem.

Daniel Mróz

unread,
Feb 7, 2008, 11:09:45 AM2/7/08
to
Daniel Mróz wrote:
> Jeśli chcesz mieć komunikację dwukierunkową w trakcie działania obu
> aplikacji, musisz się pobawić gniazdkami (uniksowymi, UDP lub TCP),
> semaforami, plikami wymiany, nazwanymi kolejkami FIFO lub innym
> wynalazkiem.
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()

Piotr Chamera

unread,
Feb 7, 2008, 11:33:48 AM2/7/08
to

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ł.

Slawomir Piasecki

unread,
Feb 8, 2008, 8:05:11 AM2/8/08
to

> 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.

def wykonaj(zadania, liczba):
j = 0
pula = []
for step in range(liczba):
if zadania:
j += 1
wr = subprocess.Popen(zadania.pop())
pula.append(wr)
print "%s -- %s" % (j, wr.pid)

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.

Daniel Mróz

unread,
Feb 8, 2008, 8:32:54 AM2/8/08
to
Slawomir Piasecki wrote:
>> 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.
Popraw konfigurację czytnika.

> 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".

Piotr Chamera

unread,
Feb 8, 2008, 9:12:46 AM2/8/08
to
Slawomir Piasecki wrote:
> 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

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 :)

Slawomir Piasecki

unread,
Feb 8, 2008, 9:40:00 AM2/8/08
to
program ktory wlepilem jest testowym na ktorym sprawdzam
co i jak dziala, stad takie wydziwasy jak powtarzajace sie 'wr'
czy dodatkowe liczniki,

>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.

Daniel Mróz

unread,
Feb 8, 2008, 10:38:34 AM2/8/08
to
Slawomir Piasecki wrote:
>>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.
Jeśli pisałeś wcześniej np. w C, to z widelcem powinieneś być zaznajomiony.
Zasada taka sama jak w StdC, czyli robisz fork(), sprawdzasz czy jesteś
dzieckiem i jedziesz z kodem, który teraz wrzucasz do zewnętrznego skryptu.
Masz cztery różne możliwości: uruchamianie liniowe, subprocess, wątki oraz
fork. Tylko Ty wiesz co będzie odpowiednie.

Slawomir Piasecki

unread,
Feb 8, 2008, 10:54:53 AM2/8/08
to

> Jeśli pisałeś wcześniej np. w C, to z widelcem powinieneś być zaznajomiony.

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.

MAcks

unread,
Feb 9, 2008, 5:36:32 AM2/9/08
to
Daniel Mróz pisze:

> 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

python...@gazeta.skasuj-to.pl

unread,
Feb 9, 2008, 6:30:01 AM2/9/08
to
Witam wszystkich ,
bardzo ciekawy wątek. Ilu pomocnych rzeczy można się z niego nauczyć!
Nie jestem osoba równie kompetentną, co przedmówcy, sam czasami zadaję tu
zupełnie podstawowe pytania, ale dorzucam swoje dwa grosze, jako że borykałem
się kilka miesięcy temu z podobnym problemem. Tzw. chciałem mieć możliwie
paralelnie działający program minimalnym kosztem - napisany w Pythonie.

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/

Daniel Mróz

unread,
Feb 10, 2008, 9:52:16 AM2/10/08
to
wrote:

> 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
Mało rozszerzalne rozwiązanie. Lepiej już użyć XMLRPC, który też jest
banalnie prosty do zakodowania, a są jego implementacje w innych językach
programowania.
Wydaje się to też armatą na muchy jeśli to samo można osiągnąć przy użyciu
wątków lub podprocesów.
Uzależniasz się także od warstwy sieciowej w systemie, która wcale nie musi
istnieć (nawet interfejsu loopback może nie być).
Dodatkowo musisz uwzględniać, że podsystem z ktorym chcesz się komunikować
może nie działać (np. się wysypał, lub wogóle nie został uruchomiony), co
wymaga dodatkowych linii kodu i logiki obsługi takich zdarzeń, co czasami
nie jest takie proste jeśli jesteś właśnie w trakcie obliczeń.

> 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ć.

python...@gazeta.skasuj-to.pl

unread,
Feb 10, 2008, 9:37:42 AM2/10/08
to
Cześć,
dzięki za wypunktowanie słabości. Oczywiście pewnie masz rację, ale w małych
projektach czysto pythonowskich nie trzeba być chyba tak dalekowzroczym...,
prawda? Tym bardziej kiedy nie mamy czasu na zaimplementowanie
super-profesjonalnego systemu rozproszonego.
Ja pisałem wyraźnie o tym, że prosty w użyciu system rozproszony można z
powodzeniem wykorzystać do wieloprocesorowości na jednym systemie. I trzeba do
tego zaledwie kilku linijek kodu. Otrzymujemy dzięki temu o niebo bardziej
elastycznie narzędzie od subprocessu, któremu można co najwyżej posłać listę
opcji i czekać aż coś zwróci na wyjściu.

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.,

Daniel Mróz

unread,
Feb 10, 2008, 12:27:24 PM2/10/08
to
wrote:

> dzięki za wypunktowanie słabości. Oczywiście pewnie masz rację, ale w
> małych projektach czysto pythonowskich nie trzeba być chyba tak
> dalekowzroczym..., prawda? Tym bardziej kiedy nie mamy czasu na
> zaimplementowanie super-profesjonalnego systemu rozproszonego.
Prosiłeś o wypunktowanie słabości, ale nie okresliłeś, że chodzi o przypadek
małego, zamkniętego projektu :)

> 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.

0 new messages