español - Python equivalente de la selección de Golang en canales




vs performance (7)

También considere la biblioteca de compensación por Benoit Chesneau. Es un puerto del modelo de concurrencia de Go para Python, que utiliza fibras debajo de las cubiertas.

Dio una presentación sobre esto en PyCon APAC 2013:

Go tiene una declaración de selección que funciona en los canales. De la documentación:

La declaración de selección permite que un goroutine espere en múltiples operaciones de comunicación.

Un bloque de selección hasta que uno de sus casos puede ejecutarse, luego ejecuta ese caso. Elige uno al azar si hay varios listos.

¿Hay un equivalente de Python del siguiente código:

package main

import "fmt"

func main() {
    c1 := make(chan int)
    c2 := make(chan int)
    quit := make(chan int)

    go func() {
        for i := 0; i < 10; i++ {
            c1 <- i
        }
        quit <- 0
    }()

    go func() {
        for i := 0; i < 2; i++ {
            c2 <- i
        }
    }()

    for {
        select {
        case <-c1:
            fmt.Println("Received value from c1")
        case <-c2:
            fmt.Println("Received value from c2")
        case <-quit:
            fmt.Println("quit")
            return
        }
    }
}

Salida de este programa:

Received value from c1
Received value from c1
Received value from c2
Received value from c1
Received value from c2
Received value from c1
Received value from c1
Received value from c1
Received value from c1
Received value from c1
Received value from c1
Received value from c1
quit

Sí, todo es posible con goless . Puedes probarlo.

Que te diviertas ;-)

Aquí hay un ejemplo:

c1 = goless.chan()
c2 = goless.chan()

def func1():
    time.sleep(1)
    c1.send('one')
goless.go(func1)

def func2():
    time.sleep(2)
    c2.send('two')
goless.go(func2)

for i in range(2):
    case, val = goless.select([goless.rcase(c1), goless.rcase(c2)])
    print(val)

Aquí hay otro, un intento de imitar la sintaxis de ir:

from threading import Thread
from Queue import Queue

def main():

    c1 = Queue.Queue(maxsize=0)
    c2 = Queue.Queue(maxsize=0)
    quit = Queue.Queue(maxsize=0)

    Thread(target=lambda: [c1.put(i) for i in range(10)] or quit.put(0)).start()
    Thread(target=lambda: [c2.put(i) for i in range(2)]).start()

    for which, msg in select(c1, c2, quit):
        if which is c1:
            print 'Received value from c1'
        elif which is c2:
            print 'Received value from c2'
        elif which is quit:
            print 'Received value from quit'
            return

def select(*queues):
    combined = Queue.Queue(maxsize=0)
    def listen_and_forward(queue):
        while True:
            combined.put((queue, queue.get()))
    for queue in queues:
        t = Thread(target=listen_and_forward, args=(queue,))
        t.daemon = True
        t.start()
    while True:
        yield combined.get()

main()

Con Python 3.5 existen las palabras clave async y await que hacen posible tener funciones que pueden suspenderse en ejecución y, por lo tanto, pueden ejecutarse en un ciclo continuo en lugar de subprocesos. El asyncio std lib está ofreciendo uno.

Para mapear más directamente el comportamiento de los canales de bloqueo de Go y select , puede utilizar esta pequeña biblioteca y luego su código de ejemplo se verá muy similar en Python.


Aquí hay una traducción bastante directa, pero la parte de "elegir cuál si hay múltiples están listas" funciona de manera diferente, simplemente toma lo que vino primero. También esto es como ejecutar su código con gomaxprocs(1) .

import threading
import Queue

def main():
    c1 = Queue.Queue(maxsize=0)
    c2 = Queue.Queue(maxsize=0)
    quit = Queue.Queue(maxsize=0)

    def func1():
        for i in range(10):
            c1.put(i)
        quit.put(0)

    threading.Thread(target=func1).start()

    def func2():
        for i in range(2):
            c2.put(i)

    threading.Thread(target=func2).start()

    combined = Queue.Queue(maxsize=0)

    def listen_and_forward(queue):
        while True:
            combined.put((queue, queue.get()))

    t = threading.Thread(target=listen_and_forward, args=(c1,))
    t.daemon = True
    t.start()
    t = threading.Thread(target=listen_and_forward, args=(c2,))
    t.daemon = True
    t.start()
    t = threading.Thread(target=listen_and_forward, args=(quit,))
    t.daemon = True
    t.start()

    while True:
        which, message = combined.get()
        if which is c1:
            print 'Received value from c1'
        elif which is c2:
            print 'Received value from c2'
        elif which is quit:
            print 'Received value from quit'
            return
main()

El cambio básico es simular la selección con hilos que combinan mensajes. Si va a utilizar mucho este patrón, puede escribir algún código de selección:

import threading
import Queue

def select(*queues):
    combined = Queue.Queue(maxsize=0)
    def listen_and_forward(queue):
        while True:
            combined.put((queue, queue.get()))
    for queue in queues:
        t = threading.Thread(target=listen_and_forward, args=(queue,))
        t.daemon = True
        t.start()
    while True:
        yield combined.get()

def main():

    c1 = Queue.Queue(maxsize=0)
    c2 = Queue.Queue(maxsize=0)
    quit = Queue.Queue(maxsize=0)

    def func1():
        for i in range(10):
            c1.put(i)
        quit.put(0)

    threading.Thread(target=func1).start()

    def func2():
        for i in range(2):
            c2.put(i)

    threading.Thread(target=func2).start()

    for which, msg in select(c1, c2, quit):
        if which is c1:
            print 'Received value from c1'
        elif which is c2:
            print 'Received value from c2'
        elif which is quit:
            print 'Received value from quit'
            return
main()

Pero...

Tenga en cuenta que esta selección no es la más adecuada, aunque no importa para su programa: una goroutina podría enviar un resultado en un canal que se colocaría en la cola en la selección y se perdería si no siempre iteráramos en el canal. Seleccione para completar!


Para completar: los canales de estilo Go, incluida la selección de trabajo, están disponibles como parte de pygolang :

ch1 = chan()    # synchronous channel
ch2 = chan(3)   # channel with buffer of size 3

def _():
    ch1.send('a')
    ch2.send('b')
go(_)

ch1.recv()      # will give 'a'
ch2.recv_()     # will give ('b', True)

_, _rx = select(
    ch1.recv,           # 0
    ch2.recv_,          # 1
    (ch2.send, obj2),   # 2
    default,            # 3
)
if _ == 0:
    # _rx is what was received from ch1
    ...
if _ == 1:
    # _rx is (rx, ok) of what was received from ch2
    ...
if _ == 2:
    # we know obj2 was sent to ch2
    ...
if _ == 3:
    # default case
    ...

El desplazamiento (ver https://.com/a/19143696/9456786 ) también parece interesante.

goless (consulte https://.com/a/39269599/9456786 ), desafortunadamente, tiene una implementación de selección débil , que por diseño no funciona correctamente en canales síncronos .


Este es un ejemplo fácil de un retraso de tiempo:

import time

def delay(period='5'):
    # If the user enters nothing, It'll wait 5 seconds
    try:
        #If the user not enters a int, I'll just return ''
        time.sleep(period)
    except:
        return ''

Otro, en Tkinter:

import tkinter

def tick():
    pass

root=Tk()
delay=100 # time in milliseconds
root.after(delay,tick)
root.mainloop()




python go