Threads are printing at the same time messing up the text output
Question:
I am using 4 threads in an application which return text I would like to print to the user.
Since I would like to avoid the threads to independently print those texts, I have created a class to administrate it.
I don’t know what I am doing wrong here, but it is still not working.
The code you can see below:
from threading import Thread
import time
import random
class Creature:
def __init__(self, name, melee, shielding, health, mana):
self.name = name
self.melee = melee
self.shielding = shielding
self.health = health
self.mana = mana
def attack(self, attacker, opponent, echo):
while 0 != 1:
time.sleep(1)
power = random.randint(1, attacker.melee)
resistance = random.randint(1, opponent.shielding)
resultant = power - resistance
if resistance > 0:
opponent.health -= resistance
if opponent.health < 0:
msg = opponent.name, " is dead"
echo.message(msg)
quit()
else:
msg = opponent.name, " lost ", resistance, " hit points due to an attack by ", attacker.name
echo.message(msg)
def healing(self, healed, echo):
while 0 != 1:
time.sleep(1)
if self.mana >= 25:
if healed.health >= 0:
if healed.health < 50:
life = random.randint(1, 50)
self.mana -= 25
healed.health += life
if healed.health > 100:
healed.health = 100
msg = healed.name, " has generated himself and now has ", self.health, " hit points"
echo.message(msg)
else:
quit()
class echo:
def message(self, msg):
print msg
myEcho = echo()
Monster = Creature("Wasp", 30, 15, 100, 100)
Player = Creature("Knight", 25, 20, 100, 100)
t1 = Thread(target = Player.attack, args = (Monster, Player, myEcho))
t1.start()
t2 = Thread(target = Monster.attack, args = (Player, Monster, myEcho))
t2.start()
t3 = Thread(target=Player.healing(Player, myEcho), args=())
t3.start()
t4 = Thread(target=Monster.healing(Monster, myEcho), args=())
t4.start()
Here you can see the messed up outputs:
*('Wasp'('Knight', ' l, ' lost ', ost 13, ' hit points ', 4, due to an attack by '' hi, 'Waspt poi')nts d
ue to an attack by ', 'Knight')
('Wasp', ' lost ', 12, ' hit points due to an attack by ', 'Knight')
('Knight', ' lost ', 17, ' hit points due to an attack by ', 'Wasp')
('Wasp', ' lost ', 6, ' hit points due to an attack by ', 'Knight'('Knight')
, ' lost ', 1, ' hit points due to an attack by ', 'Wasp')
('Wasp', ' lost ', 5, ' hit points due to an attack by ', 'Knight')
('Knight', ' lost ', 13, ' hit points due to an attack by ', 'Wasp')
(('Wa'Knighsp't', , ' los' lostt ' ', , 32, ' hit points due to an attack by ', 'Knight')
, ' hit points due to an attack by ', 'Wasp')*
Do you guys have any idea how to fix this issue?
Answers:
Use a threading.Semaphore
to ensure that there won’t be any conflicts:
screenlock = Semaphore(value=1) # You'll need to add this to the import statement.
Then, before you call echo.message
, insert this line in order to acquire the right to output:
screenlock.acquire()
and then this line after you call echo.message
so as to allow another thread to print:
screenlock.release()
Use a semaphore. An example would be:
from threading import *
screen_lock = Semaphore(value=1)
Now every time your process wants to write something, it would:
screen_lock.acquire()
print("Something!")
screen_lock.release()
More about semas here (official documentation) and here (a great article by Laurent Luce).
Slightly better than a semaphore is a re-entrant lock.
from threading import RLock
class SynchronizedEcho(object):
print_lock = RLock()
def __init__(self, global_lock=True):
if not global_lock:
self.print_lock = RLock()
def __call__(self, msg):
with self.print_lock:
print(msg)
echo = SynchronizedEcho()
echo("Test")
The benefit of a re-entrant lock is it can be used with a with
statement. That is, if any exceptions are thrown whilst using the lock you can be assured it will be released at the end of the with block. To do the same with a semaphore you would have to remember to write a try-finally block.
It’s worth noting that you should also be using a semaphore or a lock when accessing and modifying the attributes of your Creatures. This is because you have multiple threads modiying the values of the attributes. So in the same way one print was interrupted by another print and the output was garbled, so to will your attributes become garbled.
Consider the following:
Thread A
# health starts as 110
if healed.health > 100:
# Thread A is interrupted and Thread B starts executing
# health is now 90
healed.health = 100
# health is now set to 100 -- ignoring the 20 damage that was done
Thread B
# health is 110 and resistance is 20
opponent.health -= resistance
# health is now 90.
# Thread B is interrupted and Thread A starts executing
use the ‘logging’ module instead of print.
logging is thread safe, so each thread will finish to write as you expected
here you can find explanation how to use logging
about logging from python module of the week
here you can see that it is thread safe
from python doc
I am using 4 threads in an application which return text I would like to print to the user.
Since I would like to avoid the threads to independently print those texts, I have created a class to administrate it.
I don’t know what I am doing wrong here, but it is still not working.
The code you can see below:
from threading import Thread
import time
import random
class Creature:
def __init__(self, name, melee, shielding, health, mana):
self.name = name
self.melee = melee
self.shielding = shielding
self.health = health
self.mana = mana
def attack(self, attacker, opponent, echo):
while 0 != 1:
time.sleep(1)
power = random.randint(1, attacker.melee)
resistance = random.randint(1, opponent.shielding)
resultant = power - resistance
if resistance > 0:
opponent.health -= resistance
if opponent.health < 0:
msg = opponent.name, " is dead"
echo.message(msg)
quit()
else:
msg = opponent.name, " lost ", resistance, " hit points due to an attack by ", attacker.name
echo.message(msg)
def healing(self, healed, echo):
while 0 != 1:
time.sleep(1)
if self.mana >= 25:
if healed.health >= 0:
if healed.health < 50:
life = random.randint(1, 50)
self.mana -= 25
healed.health += life
if healed.health > 100:
healed.health = 100
msg = healed.name, " has generated himself and now has ", self.health, " hit points"
echo.message(msg)
else:
quit()
class echo:
def message(self, msg):
print msg
myEcho = echo()
Monster = Creature("Wasp", 30, 15, 100, 100)
Player = Creature("Knight", 25, 20, 100, 100)
t1 = Thread(target = Player.attack, args = (Monster, Player, myEcho))
t1.start()
t2 = Thread(target = Monster.attack, args = (Player, Monster, myEcho))
t2.start()
t3 = Thread(target=Player.healing(Player, myEcho), args=())
t3.start()
t4 = Thread(target=Monster.healing(Monster, myEcho), args=())
t4.start()
Here you can see the messed up outputs:
*('Wasp'('Knight', ' l, ' lost ', ost 13, ' hit points ', 4, due to an attack by '' hi, 'Waspt poi')nts d
ue to an attack by ', 'Knight')
('Wasp', ' lost ', 12, ' hit points due to an attack by ', 'Knight')
('Knight', ' lost ', 17, ' hit points due to an attack by ', 'Wasp')
('Wasp', ' lost ', 6, ' hit points due to an attack by ', 'Knight'('Knight')
, ' lost ', 1, ' hit points due to an attack by ', 'Wasp')
('Wasp', ' lost ', 5, ' hit points due to an attack by ', 'Knight')
('Knight', ' lost ', 13, ' hit points due to an attack by ', 'Wasp')
(('Wa'Knighsp't', , ' los' lostt ' ', , 32, ' hit points due to an attack by ', 'Knight')
, ' hit points due to an attack by ', 'Wasp')*
Do you guys have any idea how to fix this issue?
Use a threading.Semaphore
to ensure that there won’t be any conflicts:
screenlock = Semaphore(value=1) # You'll need to add this to the import statement.
Then, before you call echo.message
, insert this line in order to acquire the right to output:
screenlock.acquire()
and then this line after you call echo.message
so as to allow another thread to print:
screenlock.release()
Use a semaphore. An example would be:
from threading import *
screen_lock = Semaphore(value=1)
Now every time your process wants to write something, it would:
screen_lock.acquire()
print("Something!")
screen_lock.release()
More about semas here (official documentation) and here (a great article by Laurent Luce).
Slightly better than a semaphore is a re-entrant lock.
from threading import RLock
class SynchronizedEcho(object):
print_lock = RLock()
def __init__(self, global_lock=True):
if not global_lock:
self.print_lock = RLock()
def __call__(self, msg):
with self.print_lock:
print(msg)
echo = SynchronizedEcho()
echo("Test")
The benefit of a re-entrant lock is it can be used with a with
statement. That is, if any exceptions are thrown whilst using the lock you can be assured it will be released at the end of the with block. To do the same with a semaphore you would have to remember to write a try-finally block.
It’s worth noting that you should also be using a semaphore or a lock when accessing and modifying the attributes of your Creatures. This is because you have multiple threads modiying the values of the attributes. So in the same way one print was interrupted by another print and the output was garbled, so to will your attributes become garbled.
Consider the following:
Thread A
# health starts as 110
if healed.health > 100:
# Thread A is interrupted and Thread B starts executing
# health is now 90
healed.health = 100
# health is now set to 100 -- ignoring the 20 damage that was done
Thread B
# health is 110 and resistance is 20
opponent.health -= resistance
# health is now 90.
# Thread B is interrupted and Thread A starts executing
use the ‘logging’ module instead of print.
logging is thread safe, so each thread will finish to write as you expected
here you can find explanation how to use logging
about logging from python module of the week
here you can see that it is thread safe
from python doc