Seite 1 von 1

Queue equivalent?

Verfasst: Montag 5. Mai 2008, 23:04
von Jan.O
Hi,

diese Klasse erfült doch die selben funktionen wie eine Queue und ist genau so thread-save, oder liege ich da falsch? Diese simple Klasse ist nämlich 4 mal so schnell wie die implementierte "Queue".

Code: Alles auswählen

import threading

class myQueue:

	def __init__(self):
		self.lock = threading.Lock();
		self.que = []
		
	def put(self, obj):
		self.lock.acquire()
		self.que.append(obj)
		self.lock.release()
		
	def get(self):
		self.lock.acquire()
		obj = self.que.pop(0)
		self.lock.release()
		
		return obj;

Verfasst: Montag 5. Mai 2008, 23:25
von EyDu
Nee, da fehlt ganz entscheidende Funktionalität: So blockiert "Queue" bei einem Aufruf von "get" wenn keine Elemente in der Schlange vorhanden sind, das ist bei dir nicht der Fall.

Außerdem bietet "Queue" noch mehr Funktionalität, wie zum Beispiel Timeouts.

Außerdem fehlt bei dir ein try-finally in der "get"-Methode. Wenn keine Element in der Schlange hängt, dann wird eine Exception geworfen, aber der Lock nicht gelöst. Damit ist die Queue dann hinüber.

Schau dir doch einfach mal den Quellcode vom Original-"Queue" an, liegt irgendwo in deinem Python-Ordner.

Verfasst: Montag 5. Mai 2008, 23:43
von Jan.O
Ok, aber jetzt. Es kann keine exception mehr auftreten und wenn die Queue leer ist, wird gewartet.

Edit: Ne, so geht das noch nicht.

Code: Alles auswählen

import threading
class myQueue:
def __init__(self):
  self.lock = threading.Lock();
  self.emptyLock = threading.Lock();
  self.que = []
  self.count = 0
		
  def put(self, obj):
    self.lock.acquire()
    self.que.append(obj)
    
    if self.count == 0:
      self.emptyLock.release()
    
    self.count = self.count + 1
    self.lock.release()
		
	def get(self):
    self.lock.acquire()
    self.emptyLock.acquire()
      
    obj = self.que.pop(0)
    self.count = self.count - 1
    
    if self.count > 0:
      self.emptyLock.release()

    self.lock.release()
		
		return obj;


Verfasst: Dienstag 6. Mai 2008, 10:50
von EyDu

Code: Alles auswählen

class myQueue:
    def __init__(self):
        self.lock = threading.Lock()
        self.emptyLock = threading.Lock()
        self.queue = []
    
    def put(self, elem):
        self.lock.acquire()
        self.queue.append(elem)
        
        if len(self.queue) == 1:
            self.emptyLock.release()
        
        self.lock.release()
    
    def pop(self):
        try:
            self.lock.acquire()
            self.lock.emptyLock()
        
            return self.queue.pop(0)
        finally:
            if len(self.queue) == 0:
                self.emptyLock.acquire()
        
            self.lock.release()
Ist ungetestet, aber Timeouts fehlen zum Beispiel immer noch.

Verfasst: Dienstag 6. Mai 2008, 11:15
von Masaru
Ohha :shock:

NIE ein "acquire" mit folgendem Code, ohne diesen in einem try-finally-Block unterzubringen, wobei das "release" im finally-Teil abgehandelt wird.

Ihr werdet echt unglücklich, wenn Ihr mit mehrere Threads arbeitet, und einer mal sich abschiesst. Das herauszufinden ist nicht gerade lustig ;).

Bsp.-Konstrukt:

Code: Alles auswählen

import threading 

class Sample(object):
    def __init__(self):
        self.__lock = threading.Lock()

    def get(self):
        self.__lock.acquire()
        try:
            # hier die Anwendungslogic
        finally:
            self.__lock.release()
>>Masaru<<

Verfasst: Dienstag 6. Mai 2008, 11:24
von EyDu
Das ist doch nur ein Beispiel, ich war lediglich zu faul, das Ergebnis zwischenzuspeichern. Jetzt weiss ich aber, warum "from Tkinter import *" so verbreitet ist...

Verfasst: Dienstag 6. Mai 2008, 11:35
von mitsuhiko
Die Queue vom ersten Beispiel hat keinen Vorteil gegenüber list. :wink: Wenn man sowas will nimmt man collections.deque. (Double-ended queue)

Verfasst: Dienstag 6. Mai 2008, 22:38
von Mad-Marty
Glaubs ruhig die Queue.Queue ist wirklich gut.

Das was mir u.a. fehlte war die möglichkeit das 1. element zu inspizieren ohne es herauszunehmen und persistent syncs bei jeder änderung auf die HDD.


Am besten du versuchst nicht eine eigene Queue zu bauen wenn nicht nötig,
gerade wenn es mit echten threads arbeitet und nicht nur 1 lock beachtet werden muss, musst du extrem viele möglichkeiten durchdenken (was wenn ... X ... in Y eine Exception hat ... etc).

Außer du hast Zeit ... dann kann das mal ganz nett sein :P