Pregunta

Estoy escribiendo un pequeño rastreador que debería obtener una URL varias veces, quiero que todos los hilos funcionen al mismo tiempo (simultáneamente).

He escrito un pequeño código que debería hacer eso.

import thread
from urllib2 import Request, urlopen, URLError, HTTPError


def getPAGE(FetchAddress):
    attempts = 0
    while attempts < 2:
        req = Request(FetchAddress, None)
        try:
            response = urlopen(req, timeout = 8) #fetching the url
            print "fetched url %s" % FetchAddress
        except HTTPError, e:
            print 'The server didn\'t do the request.'
            print 'Error code: ', str(e.code) + "  address: " + FetchAddress
            time.sleep(4)
            attempts += 1
        except URLError, e:
            print 'Failed to reach the server.'
            print 'Reason: ', str(e.reason) + "  address: " + FetchAddress
            time.sleep(4)
            attempts += 1
        except Exception, e:
            print 'Something bad happened in gatPAGE.'
            print 'Reason: ', str(e.reason) + "  address: " + FetchAddress
            time.sleep(4)
            attempts += 1
        else:
            try:
                return response.read()
            except:
                "there was an error with response.read()"
                return None
    return None

url = ("http://www.domain.com",)

for i in range(1,50):
    thread.start_new_thread(getPAGE, url)

De los registros de Apache, parece que los hilos se están ejecutando simultáneamente, hay un poco de brecha entre las solicitudes, es casi indetectable, pero puedo ver que los hilos no son realmente paralelos.

He leído sobre Gil, ¿hay alguna manera de evitarlo sin llamar a un código C C ++? Realmente no puedo entender cómo es posible el enhebrado con Gil. ¿Python básicamente introduce el siguiente hilo tan pronto como termina con el anterior?

Gracias.

¿Fue útil?

Solución

Como señala, el Gil a menudo evita que los hilos de Python funcionen en paralelo.

Sin embargo, ese no es siempre el caso. Una excepción es el código de I/O. Cuando un hilo está esperando una solicitud de E/S para completar, generalmente habría lanzado el GIL antes de ingresar a la espera. Esto significa que otros hilos pueden progresar mientras tanto.

En general, sin embargo, multiprocessing es la apuesta más segura cuando se requiere un verdadero paralelismo.

Otros consejos

He leído sobre Gil, ¿hay alguna manera de evitarlo sin llamar a un código C C ++?

Realmente no. Las funciones llamadas a través de CTYPES lanzarán el GIL durante la duración de esas llamadas. Las funciones que realizan E/S de bloqueo también lo lanzarán. Hay otras situaciones similares, pero siempre involucran código fuera del bucle principal del intérprete de Python. No puedes dejar ir al Gil en tu código Python.

Puede usar un enfoque como este para crear todos los hilos, hacer que esperen un objeto de condición y luego hacer que comiencen a buscar la URL "simultaneamente":

#!/usr/bin/env python
import threading
import datetime
import urllib2

allgo = threading.Condition()

class ThreadClass(threading.Thread):
    def run(self):
        allgo.acquire()
        allgo.wait()
        allgo.release()
        print "%s at %s\n" % (self.getName(), datetime.datetime.now())
        url = urllib2.urlopen("http://www.ibm.com")

for i in range(50):
    t = ThreadClass()
    t.start()

allgo.acquire()
allgo.notify_all()
allgo.release()

Esto te acercaría un poco más a que todas las ofertas ocurran al mismo tiempo, PERO:

  • Los paquetes de red que salen de su computadora pasarán el cable Ethernet en secuencia, no al mismo tiempo,
  • Incluso si tiene más de 16 núcleos en su máquina, es probable que algunos enrutadores, puentes, módem u otros equipos entre su máquina y el host web tengan menos núcleos, y puede serializar sus solicitudes,
  • El servidor web del que está obteniendo cosas utilizará un accept() llame para responder a su solicitud. Para un comportamiento correcto, eso se implementa utilizando un bloqueo de servidor-global para garantizar que solo un proceso de servidor/subproceso responda a su consulta. Incluso si algunas de sus solicitudes llegan al servidor simultaneamente, esto causará cierta serialización.

Probablemente recibirá sus solicitudes a superposición en mayor grado (es decir, otros que comienzan antes de terminar), pero nunca recibirás todas tus solicitudes empezar simultáneamente en el servidor.

También puede ver cosas como el futuro de Pypy, donde tendremos memoria de transición de software (por lo tanto, eliminan el GIL) todo esto es solo investigación y burla intelectual en este momento, pero podría convertirse en algo grande.

Si ejecuta su código con Jython o Ironpython (y tal vez Pypy en el futuro), se ejecutará en paralelo

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top