Seite 1 von 1
Multithread Windows and Linux kompatibel
Verfasst: Dienstag 27. Mai 2008, 08:10
von tschinz
Hallo Leute,
Ich möchte ein sozusagen Toplevel skript schreiben welches andere Pythonskripts aufruft und threads dieser skripts erstellt. Dieses System sollte unter windows sowie unter Linux funktionieren ohne etwas am code zu ändern.
eigentlich funktioniert es, habe aber einen Bug bemerkt. Ich erstelle von einem Skript 2 "thread instanzen" wobei ich bemerkt habe das diese alle globalen (self.name) variablen gemeinsam benutzten. Ich möchte aber das jeder thread seine eigenen Variablen benutzt.
Hat jemand eine Idee oder eine Anmerkung!
hier ein kleiner Ausschnitt wie ich die Threads der Skripte erstelle:
Code: Alles auswählen
cwd = os.getcwd()
sys.path.append(cwd)
print('Adding current path to PYTHONPATH: ' + cwd)
import GFP.source
thread.start_new_thread(GFP.source.run, (False,
VCAT_SOURCE_HOST_LOCAL,
DATA_FROM_GFP_PORT_LOCAL,
VCAT_SOURCE_FILE0,
GFP_BASE_DIR)))
thread.start_new_thread(GFP.source.run, (False,
VCAT_SOURCE_HOST REMOTE,
DATA_FROM_GFP_PORT_REMOTE,
VCAT_SOURCE_FILE1,
GFP_BASE_DIR)))
Meine Ordnersturkur sieht folgender massen aus.
Toplevelskript.py
+---> GFP
+--->source.py
+--->__init__.py
Danke für jeglich Hilfe
tschinz
Re: Multithread Windows and Linux kompatibel
Verfasst: Dienstag 27. Mai 2008, 08:19
von keppla
tschinz hat geschrieben:eigentlich funktioniert es, habe aber einen Bug bemerkt. Ich erstelle von einem Skript 2 "thread instanzen" wobei ich bemerkt habe das diese alle globalen (self.name) variablen gemeinsam benutzten. Ich möchte aber das jeder thread seine eigenen Variablen benutzt.
erstmal ist self.name keine globale variable, und zum zweiten ist das kein Bug sondern ein Feature, Threads funktionieren so: sie teilen sich einen Speicherbereich. Wenn die Threads auf unterschiedliche objekte zugreifen sollen, gib ihnen unterschiedliche Objekte.
Verfasst: Dienstag 27. Mai 2008, 09:06
von tschinz
Hallo keppla,
Dies bedeuted wenn ich in einem skript im main ein self.name objekt erstelle. und 2 instanzen dieses main's (bzw. skripts) erstelle, greifen beide auf das selbe self.name zu.
Dieses self.name wird aber erst im main erstellt.
In dem Fall lautet meine Frage anders. wie erstelle ich im Objektorientierten sinne eine private variabel einer Klasse (nur innerhalb der klasse gültig). (Falls ich also 2 instanzen der Klasse habe sollten auch für jede ihre eigene vriabel haben).
Danke nochmals für die Hilfe
tschinz
Verfasst: Dienstag 27. Mai 2008, 09:35
von tschinz
Hallo,
Also ich habe ein bisschen probiert. Es ist egal ob ich eine self. oder eine "normale" Variabel habe. Falls ich 2 instanzen bzw. 2 threads der gleichen klasse erstelle mit der oben erwähnten methode. Habe beide threads die genau gleichen variabeln bzw. objekte. Wie keppla gesagt hat beie threads benutzten den gleichen speicherbereich.
WIE KANN ICH DAS VERMEIDEN. Ich möchte 2 komplett getrennte threads eines skripts erstellen. Beide instanzen sollen parallel ablaufen.
Probiere jetzt schon zimlich eine weile und komme einfach nicht voran....
tschinz
Verfasst: Dienstag 27. Mai 2008, 09:39
von keppla
tschinz hat geschrieben:Hallo keppla,
Dies bedeuted wenn ich in einem skript im main ein self.name objekt erstelle.
Deine Nomenklatur ist etwas ungenau hier, könntest du ein codebeispiel posten? Was meinst du mit "ein self.name object"
und 2 instanzen dieses main's (bzw. skripts) erstelle, greifen beide auf das selbe self.name zu.
wie erstellst du zwei instanzen eines skripts? Instanzen erstellt man von Objekten.
Du rufst in deinem Beispiel zweimal die selbe Funktion (GFP.source.run) auf, die die selben Argumente bekommt. Dass die dasselbe machen, und auf dieselben globalen Variablen zugreifen, ist nichts besonderes.
Mal anders gefragt, was ist dein eigentliches Ziel? Das Threading ist doch wahrscheinlich nur mittel zum Zweck.
Verfasst: Dienstag 27. Mai 2008, 09:49
von keppla
Ich befürchte, du solltest dir noch mal genauer angucken, wie imports bei python funktionieren, Ich glaube, du erwartest aus der Tatsache, dass etwas in eine andere Datei ausgelagert, Dinge, die nicht zutreffen.
tschinz hat geschrieben:Falls ich 2 instanzen bzw. 2 threads der gleichen klasse erstelle mit der oben erwähnten methode.
Das tust du nicht. Du erstellst weder 2 instanzen der gleichen Klasse (welcher Klasse überhaupt), noch kann man "threads einer Klasse" erstellen.
Um mal ein Beispiel zu bringen:
Code: Alles auswählen
class Counter:
def __init__(self):
self.value = 0
def inc(self):
self.value += 1
a = Counter()
b = Counter()
c = Counter()
from threading import Thread
def f(c1, c2):
for i in range(100):
c1.inc()
c2.inc()
t1 = Thread(target=f, args=(a, b))
t1.start()
t2 = Thread(target=f, args=(a, c))
t2.start()
print a.value, b.value, c.value
Verfasst: Dienstag 27. Mai 2008, 09:52
von tschinz
Ich habe viele verschiedene skripts. Alls kommunizieren untereinander mit TCP sockets.
Bis jetzt habe ich die skripts unter Linux mit einem shell aufgerufen, was wiefolgt ausgesehen hat.
Code: Alles auswählen
$GFP_BASE_DIR/source.py \
--VCAT_host $VCAT_SOURCE_HOST_LOCAL \
--VCAT_port $DATA_FROM_GFP_PORT_LOCAL \
--infile $VCAT_SOURCE_FILE \
--basedir $GFP_BASE_DIR &
childPids=( "${childPids[@]}" $! )
$GFP_BASE_DIR/source.py \
--VCAT_host $VCAT_SOURCE_HOST_REMOTE \
--VCAT_port $DATA_FROM_GFP_PORT_REMOTE \
--infile $VCAT_SOURCE_FILE \
--basedir $GFP_BASE_DIR &
childPids=( "${childPids[@]}" $! )
Dieser code führt 2 mal das source.py aus beide laufen parallel und sind unter Linux ein eigener Process.
Dadurch das es jetzt auch unter windows laufen soll ohne irgendwelche installation(ausser python), wollte ich das shell skript duch eine sozusagen Toplevel pythonskript ersetzten.
Dafür habe ich in jedem skript die methode run erstellt wo ich unteranderem ein Objekt er klasse erstelle (source.py hat eine run wo die Klasse source instanziert wird). Nun scheint es als ob ich 2 mal das gleiche objekt benutzte.
Wie kann man das lösen. wie gesagt alle skipts sollten parallel laufen.
Hier noch ein posting meines python codes source.py
Code: Alles auswählen
import sys
def run(verbose,
VCAT_host, VCAT_port,
input_filespec, base_directory):
if VCAT_host == -1:
VCAT_host = 'localhost'
if VCAT_port == -1:
VCAT_port = 16000
if base_directory == -1:
base_directory = '.'
if input_filespec == -1:
input_filespec = 'ITUT_G7042_LCAS.pdf'
input_filespec = base_directory + '/' + input_filespec
if verbose:
print "\nGenerating data for a VCAT flow"
print indent, 'VCAT source host is', VCAT_host
print indent, 'VCAT source port is', VCAT_port
print indent, 'Basedir is', base_directory
print indent, 'Input file is', input_filespec
print ''
#-------------------------------------------------------------------------------
# Instanciate an object of GFP data source class
#
if verbose:
verbosity = 1
else:
verbosity = None
source = GFP_source('file data',
VCAT_host,
VCAT_port,
input_filespec,
verbosity)
################################################################################
# Define the GFP data source class
#
# define project libraries
from channel.tx import Tx
from channel.thread import Thread
from channel.common import DataHandler
from channel.common import Error
from channel.api import *
from Common.VCAT_LCAS import *
# define the GFP data source class
class GFP_source(Thread):
def __init__(self,
name,
Tx_host,
Tx_port,
filespec,
verbosity = None):
Thread.__init__(self, name, verbosity)
#---- create the output channel and start connections
self.output = Tx(self,
'out',
data_from_GFP_request_nb,
(Tx_host, Tx_port),
verbosity)
self.start()
#---- run this IP's core function
try:
self.main(filespec)
except Error:
self.msg('Error in "main", time to leave.', -2)
self.kill(True)
#--------------------------------------------------- data generation from file
def generate_data(self):
new_block = self.STREAM.read(GFP_packet_length)
return new_block
#================================================================ data queuing
def main(self, input_filespec):
#----INITIALISATION
#---- open the data file
self.STREAM = open(input_filespec, 'rb')
#---- START WORKING
#---- prepare packets to be requested by the sink
output_buffers = []
for index in range(self.output.size):
new_block = self.generate_data()
self.msg('Preparing to send ' + repr(new_block))
dh = DataHandler(new_block)
output_buffers.append(dh)
Send(self.output, dh, data_from_GFP_message_length)
output_buffers_pointer = 0
#---- main loop: prepare a new packet as one has been sent
while True:
#---- wait for a message to be sent
Wait(self.output)
#---- prepare a new packet
new_block = self.generate_data()
self.msg('Preparing to send ' + repr(new_block))
#---- queue it on the output channel
dh = output_buffers[output_buffers_pointer]
dh.setData(new_block)
Send(self.output, dh, data_from_GFP_message_length)
#---- increment the buffer pinter modulo the buffer length
output_buffers_pointer = (output_buffers_pointer + 1) % self.output.size
Danke
tschinz
Verfasst: Dienstag 27. Mai 2008, 10:07
von keppla
Dieser code führt 2 mal das source.py aus beide laufen parallel und sind unter Linux ein eigener Process.
äh, nein, eher nicht. Deine Shell ist ein Prozess, und die dürfte zwei kinderprozesse haben, nämlich die beiden pythonintepreter.
tschinz hat geschrieben:Bis jetzt habe ich die skripts unter Linux mit einem shell aufgerufen
Du hast also zwei verschiedene Prozesse gestartet, mit jeweils einem eigenen interpreter.
Dein Beispiel tut aber etwas völlig anderes, es startet einen Prozess mit einem Interpreter, in dem sich zwei Threads tummeln, und auf die gleichen Variablen zugreifen. Was, wie gesagt, vollkommen zu erwarten ist, wenn einem der unterschied zwischen Threads und Prozessen klar ist.
Könntest du nicht einfach das Subprocess-modul nutzen? Mit dem könntest du die zwei skripte in neuen Prozessen starten, so wie du es früher in der Shell gemacht hast.
Verfasst: Dienstag 27. Mai 2008, 10:21
von tschinz
du meinst etwas wie:
p = Popen(["mycmd", "myarg"])
Eine frage dazu habe ich noch. Wie erfahre ich die Pid dieses processes damit ich ihn am ende wieder beenden kann? Bzw. wie beende ich ihn am schluss?
Oder noch besser, könntest du mir eine kleines posting machen mit einem Popen beispiel.
Und vielen dank für die Hilfe, trotz meiner schlechte Nomenklatur, bin erst sein ein paar wochen in Python drin.
tschinz
Verfasst: Dienstag 27. Mai 2008, 10:28
von keppla
tschinz hat geschrieben:du meinst etwas wie:
p = Popen(["mycmd", "myarg"])
Ja, genau.
Eine frage dazu habe ich noch. Wie erfahre ich die Pid dieses processes damit ich ihn am ende wieder beenden kann?
http://docs.python.org/lib/node532.html
die Pid bekommst du bequemerweise mit p.pid
Bzw. wie beende ich ihn am schluss?
keine Ahnung. Ich glaube, der elegantere weg wäre, wenn die Skripte sich selber beenden. Ich hab keine Ahnung, ob es überhaupt eine Art "plattformübergreifendes" SIGTERM gibt.
Oder noch besser, könntest du mir eine kleines posting machen mit einem Popen beispiel.
Dein Beispiel oben ist eigentlich schon vollständig, es sollte einen Process starten.
Verfasst: Dienstag 27. Mai 2008, 10:57
von tschinz
Also, habe mal eine testklasse und eine Test toplevel erstellt um das ganze zu testen.
Aber es schient nicht zu funktionieren, irgendwo mache ich einen Fehler.
hier mein code
toplevelfile welches ich mit einem Interpreter ausführe:
Code: Alles auswählen
import sys, time, os
#-------------------------------------------------------------------------------
# Add local directory to the Python path
cwd = os.getcwd()
sys.path.append(cwd)
print('Adding current path to PYTHONPATH: ' + cwd)
cmd = 'python class1.py 1'
arg = '1'
p = os.popen(cmd)
z = os.popen(cmd)
#Started all thread
while(1):
time.sleep(1)
Danach das skript welches eine testklasse Class1 enthält:
Code: Alles auswählen
import sys
class class1:
def __init__(self, verbosity):
print ('started')
print (repr(verbosity))
self.main(verbosity)
def main(self, verbosity):
self.globalvar = 0
while True:
if verbosity:
print 'class 1 says hello and globalvar = ' + repr(self.globalvar)
self.globalvar = self.globalvar+1
if self.globalvar == 5000:
sys.exit(1)
minArgs = 1
if len(sys.argv) > minArgs:
verbosity = int(sys.argv[minArgs])
else:
verbosity = None
s = class1(verbosity)
Die funktion popen wird ausgeführt aber nahher dreht er in der while des toplevels, und nichts wird angezeigt von den processen der class1.
Weisst du was nicht korekt ist.
/tschinz
Verfasst: Dienstag 27. Mai 2008, 11:00
von tschinz
sorry ich habe vergessen zu erwähnen das ich die processe sehe im processmanager von Linux aber wo ist das output, ich möchte den output eigentlich im shell sehen wo ich das toplevel-skript gestarted habe.
/tschinz
Verfasst: Dienstag 27. Mai 2008, 11:01
von Leonidas
``os.popen`` != ``subprocess.Popen``.
Verfasst: Dienstag 27. Mai 2008, 11:11
von tschinz
ups mein fehler. bekomme aber trotzdem einen Error.
Der code ist gleich wie oben ausser
Code: Alles auswählen
cmd = 'python class1.py'
arg = '1'
p = subprocess.Popen([cmd, arg])
Der Error ist:
Adding current path to PYTHONPATH: /home/zas/workspace/threadtest
Traceback (most recent call last):
File "vcat-lcas.py", line 23, in <module>
p = subprocess.Popen([cmd,arg])
File "/usr/lib/python2.5/subprocess.py", line 593, in __init__
errread, errwrite)
File "/usr/lib/python2.5/subprocess.py", line 1135, in _execute_child
raise child_exception
OSError: [Errno 2] No such file or directory
class1.py ist im gleichen ordner wie das toplevel skript und im pythonpath
/home/zas/workspace/threadtest
Danke für die Unterstützung
/tschinz
Verfasst: Dienstag 27. Mai 2008, 11:17
von Leonidas
Das erste Element ist der Name des Binaries, du hast kein Binary das "python class1.py" heißt. Binaries düfen auch Spaces enthalten, daher der Fehler.
Verfasst: Mittwoch 28. Mai 2008, 15:42
von tschinz
Das hat das problem gelöst, funktioniert auf Windows und Linux. Danke nochmals für die schnelle und Kompetente Hilfe.
tschinz