Multithread Windows and Linux kompatibel

Wenn du dir nicht sicher bist, in welchem der anderen Foren du die Frage stellen sollst, dann bist du hier im Forum für allgemeine Fragen sicher richtig.
Antworten
tschinz
User
Beiträge: 9
Registriert: Dienstag 27. Mai 2008, 07:49

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
Benutzeravatar
keppla
User
Beiträge: 483
Registriert: Montag 31. Oktober 2005, 00:12

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.
tschinz
User
Beiträge: 9
Registriert: Dienstag 27. Mai 2008, 07:49

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
tschinz
User
Beiträge: 9
Registriert: Dienstag 27. Mai 2008, 07:49

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
Benutzeravatar
keppla
User
Beiträge: 483
Registriert: Montag 31. Oktober 2005, 00:12

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.
Benutzeravatar
keppla
User
Beiträge: 483
Registriert: Montag 31. Oktober 2005, 00:12

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
tschinz
User
Beiträge: 9
Registriert: Dienstag 27. Mai 2008, 07:49

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
Benutzeravatar
keppla
User
Beiträge: 483
Registriert: Montag 31. Oktober 2005, 00:12

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.
tschinz
User
Beiträge: 9
Registriert: Dienstag 27. Mai 2008, 07:49

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
Benutzeravatar
keppla
User
Beiträge: 483
Registriert: Montag 31. Oktober 2005, 00:12

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.
tschinz
User
Beiträge: 9
Registriert: Dienstag 27. Mai 2008, 07:49

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
Zuletzt geändert von tschinz am Dienstag 27. Mai 2008, 11:01, insgesamt 1-mal geändert.
tschinz
User
Beiträge: 9
Registriert: Dienstag 27. Mai 2008, 07:49

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
Leonidas
Python-Forum Veteran
Beiträge: 16025
Registriert: Freitag 20. Juni 2003, 16:30
Kontaktdaten:

``os.popen`` != ``subprocess.Popen``.
My god, it's full of CARs! | Leonidasvoice vs (former) Modvoice
tschinz
User
Beiträge: 9
Registriert: Dienstag 27. Mai 2008, 07:49

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
Leonidas
Python-Forum Veteran
Beiträge: 16025
Registriert: Freitag 20. Juni 2003, 16:30
Kontaktdaten:

Code: Alles auswählen

subprocess.Popen(['python', 'class1.py', '1'])
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.
My god, it's full of CARs! | Leonidasvoice vs (former) Modvoice
tschinz
User
Beiträge: 9
Registriert: Dienstag 27. Mai 2008, 07:49

Das hat das problem gelöst, funktioniert auf Windows und Linux. Danke nochmals für die schnelle und Kompetente Hilfe.

tschinz
Antworten