Das Konzept *"concurrent programming"* ist eine Sammlung von Techniken, um innerhalb eines Programmes mehrere Aufgaben unabhängig -- oder fast unabhängig -- voneiner bearbeiten zu können. Auf entsprechenden Machinen, bzw. in einem Verbund von Computern, können diese Aufgaben wenn möglich auch gleichzeitig ausgeführt werden - das nennt sich *"parallel computing"*.
Eine der einfachsten Formen von concurrency sind Co-Routinen. Eine Co-Routine verhält sich hierbei ganz ähnlich zu einer Funktion, welche aber mehrmals hintereinander aufgerufen werden kann (wenn sie sich intern wiederholt). Zu jedem Aufruf gibt es ein dazugehöriges Ergebnis oder eine Exception, die andeutet dass die Co-Routine zu Ende ist.
Entscheidender Unterschied ist das yield
-Statement: Es kann an dieser Stelle ein Zwischenergebnis zurückgeben oder auf die Eingabe eines Wertes warten.
Verwendet werden Co-Routinen, indem sie zuerst Instanziert werden und dann entweder als Iterator zum Einsatz kommen oder explizit .next()
zum Abrufen des nächsten Wertes aufgerufen wird.
Wichtig ist zu verstehen, dass Co-Routinen nicht erst beim Aufrufen des .next()
calls zum Arbeiten beginnen.
Sie können selbständig im Hintergrund schon so lange beschäftigt sein,
bis sie an der Stelle des yield
-Statements ein fertiges Ergebnis zum Abruf bereit halten.
Hier einfache Beispiele:
def coroutine1():
yield "abc"
yield "xyz"
yield 777
c1 = coroutine1()
print(next(c1))
print(next(c1))
print(next(c1))
print(next(c1))
abc xyz 777
StopIterationTraceback (most recent call last) <ipython-input-4-ac681be2fb60> in <module>() 3 print(next(c1)) 4 print(next(c1)) ----> 5 print(next(c1)) StopIteration:
def even_numbers(start_value = 0):
x = start_value
while True:
x += 1
if x % 2 == 0:
yield x
en = even_numbers(21)
for i in range(5):
print(next(en))
22 24 26 28 30
Da Co-Routinen eine spezielle Form von Iteratoren sind,
brechen sie in diesem Fall hier nicht von selbst ab.
Wir müssen daher in dieser for i in en2
Schleife sicherstellen,
dass irgendwann break
aufgerufen wird!
en2 = even_numbers(1)
for i in en2:
print(i)
if i > 10:
break
2 4 6 8 10 12
variable = yield
und coroutine_instance.send(arg)
erlauben,
Objekte an eine Co-Routine zu schicken.
Achtung, das Timing von next
und send
darf nicht aus dem Takt kommen!
def triple():
while True:
print(" Ready to recieve values")
y = yield
print(" I got %s" % y)
yield y * 3
t = triple()
next(t) # initialisierung der Co-Routine!
print("init fertig")
z = t.send(21)
print("habe %s erhalten" % z)
next(t)
z = t.send(-4)
print("habe %s erhalten" % z)
Ready to recieve values init fertig I got 21 habe 63 erhalten Ready to recieve values I got -4 habe -12 erhalten
Bonus: Verkettungen
def oddvals():
x = 1
while True:
yield x
x += 2
def sumall(co_other):
sum = 0
while True:
sum += next(co_other)
yield sum
a1 = oddvals()
sa = sumall(a1)
for i in range(10):
print(next(sa))
1 4 9 16 25 36 49 64 81 100
Kontrolle:
[sum(range(1, 2 * x, 2)) for x in range(1, 11)]
[1, 4, 9, 16, 25, 36, 49, 64, 81, 100]
from threading import Thread
from queue import Queue
def calculate(name, input_queue, return_queue):
sum = 0
while True:
c = input_queue.get()
print("working in " + name)
if c < 0:
break
sum += c
return_queue.put(sum)
input_queue = Queue()
return_queue = Queue()
t1 = Thread(target=calculate, args=("T1", input_queue, return_queue))
t2 = Thread(target=calculate, args=("T2", input_queue, return_queue))
t1.start()
t2.start()
for k in range(99999, 99999+20):
input_queue.put(k)
input_queue.put(-1)
input_queue.put(-1)
working in T1 working in T1 working in T1 working in T1 working in T1 working in T1 working in T1 working in T1 working in T1 working in T1 working in T1 working in T1 working in T1 working in T1 working in T1 working in T1 working in T1 working in T1 working in T1 working in T1 working in T1 working in T2