Is this python code thread-safe? - python

I am trying to make my chunk of code non-thread-safe, in order to toy with some exceptions that I want to add on later.
This is my python code:
from time import sleep
from decimal import *
from threading import Lock
import random
def inc_gen(c):
"""
Increment generator
"""
while True:
#getting sleep period
timing_rand = random.randrange(0,1000)
print "INC: Sleeping for " + str(Decimal(timing_rand)/Decimal(1000))
sleep(Decimal(timing_rand)/Decimal(1000))
c.inc()
yield c
def dec_gen(c):
"""
decrement generator
"""
while True:
#getting sleep period
timing_rand = random.randrange(0,1000)
print "DEC: Sleeping for " + str(Decimal(timing_rand)/Decimal(1000))
sleep(Decimal(timing_rand)/Decimal(1000))
c.dec()
yield c
class something():
"""
We use an obj instead of an atomic variable c, we can have "threads"
simulating shared resources, instead of a single variable, to avoid
atomic instructions. (which is thread-safe in python thanks to GIL)
"""
def __init__(self):
self.c = 0
def inc(self):
self.c += 1
def dec(self):
self.c -= 1
def value(self):
return self.c
def main():
"""
main() function
"""
obj = something()
counters = [inc_gen(obj),dec_gen(obj)]
#we only want inc_gen 10 times, and dec_gen 10 times.
inc = 0 #number of times inc_gen is added
dec = 0 #number of times dec_gen is added
while True:
#choosing the next counter
if inc < 10 and dec < 10:
counter_rand = random.randrange(0,2)
if counter_rand == 0:
inc += 1
else: dec += 1
elif inc < 10 and dec == 10:
inc += 1
counter_rand = 0
elif dec < 10 and inc == 10:
dec += 1
counter_rand = 1
else: break
counters[counter_rand].next()
#print for testing
print "Final value of c: " + str(obj.value())
if __name__ == "__main__":
main()
What I want it to do is to have the code possibly result in a final value which is not 0.
Is it thread-safe? If not, how can I make it such that it is not thread-safe?

You have a Read-Modify-Write operation basically. If you want to ensure things go haywire, the best is to intruduce delay between the read and the write.
def inc(self):
v = self.c
time.sleep(random.random()) # Should probably limit it to a few hundred ms
self.c = v + 1
def dec(self):
v = self.c
time.sleep(random.random()) # Should probably limit it to a few hundred ms
self.c = v - 1

Related

Start and pause a process in python

I have a function that calculates the Fibonacci numbers. I am trying to add a feature that sends a signal indicating that I need to pause the function running. Once I get the signal, I store away all the numbers calculated thus far.
And when the user wants to resume the function, I can load back the files and start calculating from where we had stopped.
This is the code I have
MAX = 100000
fibo_series = [0] * MAX
import pickle
def fib(start_value=0, stop_value=1000, stop_signal=False):
if not stop_signal:
for i in range(start_value, stop_value):
if i == 0:
fibo_series[i] = 0
elif i == 1 or i == 2:
fibo_series[i] = 1
else:
fibo_series[i] = fibo_series[i-1] + fibo_series[i-2]
return fibo_series[i]
else:
with open('fibo_series.pkl', 'wb') as f:
pickle.dump(fibo_series, f)
def reload_fib():
with open('fibo_series.pkl', 'rb') as f:
fibo_series = pickle.load(f)
start_value = fibo_series[1:].index(0) + 1
stop_value = len(fibo_series)
return fib(start_value, stop_value, stop_signal=False)
The part that I am not able to figure out is how to use this stop_signal once the function has started running. Once the function has started the value stop_signal is locked. The only way I know to pause a function is by terminating it which is not what I am trying to do.
How do I achieve this?
I am not sure what are the stop and run conditions, but a simple work around is to use a global variable for these two functions. This is not better than using a lock of a thread library but if your work is simply to use one process and does not need complexity, then you may try.
import threading
import time
MAX = 100000
fibo_series = [0] * MAX
import pickle
stop_signal = False
def fib(start_value=0, stop_value=1000):
for i in range(start_value, stop_value):
if (not stop_signal):
print('here', i)
time.sleep(1)
if i == 0:
fibo_series[i] = 0
elif i == 1 or i == 2:
fibo_series[i] = 1
else:
fibo_series[i] = fibo_series[i-1] + fibo_series[i-2]
else:
break
return fibo_series[i]
print('exiting... stop_signal: ', stop_signal)
with open('fibo_series.pkl', 'wb') as f:
pickle.dump(fibo_series, f)
def reload_fib():
with open('fibo_series.pkl', 'rb') as f:
fibo_series = pickle.load(f)
start_value = fibo_series[1:].index(0) + 1
stop_value = len(fibo_series)
global stop_signal
stop_signal = False
return fib(start_value, stop_value)
x = threading.Thread(target=fib )
x.start()
time.sleep(10)
stop_signal = True
print('finished...')
Then, whenever the user wants to stop the execution, you can set the global variable stop_signal=True
and the output is:
here 0
here 1
here 2
here 3
here 4
here 5
here 6
here 7
here 8
here 9
finished...
I hope this helps.

My Python interpreter for Self-modifying Brainf*** has a bug

I wrote this Python interpreter for a language called Self-modifying Brainf*** (SMBF). Today I discovered a bug where if the program dynamically creates code at the initial cell or after on the tape, it will not be executed. I wrote this interpreter to look as close as possible to the Ruby interpreter on the linked page. Note that this bug may exist in the original Ruby interpreter, too. I don't know, I haven't used it.
The way SMBF is different from normal BF is that the source code is placed on the tape to the left of the cell that the pointer starts at. So the program <. would print the last character of the source (a period). This works.
Note that I trimmed some code out so it's still runnable but takes less space in this post.
The interpreter:
from __future__ import print_function
import os, sys
class Tape(bytearray):
def __init__(self):
self.data = bytearray(b'\0' * 1000)
self.center = len(self.data) // 2
def __len__(self):
return len(self.data)
def __getitem__(self, index):
try:
return self.data[index + self.center]
except:
return 0
def __setitem__(self, index, val):
i = index + self.center
if i < 0 or i >= len(self.data):
# resize the data array to be large enough
new_size = len(self.data)
while True:
new_size *= 2
test_index = index + (new_size // 2)
if test_index >= 0 and test_index < new_size:
# array is big enough now
break
# generate the new array
new_data = bytearray(b'\0' * new_size)
new_center = new_size // 2
# copy old data into new array
for j in range(0, len(self.data)):
new_data[j - self.center + new_center] = self.data[j]
self.data = new_data
self.center = new_center
self.data[index + self.center] = val & 0xff
class Interpreter():
def __init__(self, data):
self.tape = Tape()
# copy the data into the tape
for i in range(0, len(data)):
self.tape[i - len(data)] = data[i]
# program start point
self.entrypoint = -len(data)
def call(self):
pc = self.entrypoint
ptr = 0
# same as -len(self.tape) // 2 <= pc + self.tape.center < len(self.tape) // 2
while -len(self.tape) <= pc < 0: # used to be "while pc < 0:"
c = chr(self.tape[pc])
if c == '>':
ptr += 1
elif c == '<':
ptr -= 1
elif c == '+':
self.tape[ptr] += 1
elif c == '-':
self.tape[ptr] -= 1
elif c == '.':
print(chr(self.tape[ptr]), end="")
elif c == ',':
sys.stdin.read(1)
elif c == '[':
if self.tape[ptr] == 0:
# advance to end of loop
loop_level = 1
while loop_level > 0:
pc += 1
if chr(self.tape[pc]) == '[': loop_level += 1
elif chr(self.tape[pc]) == ']': loop_level -= 1
elif c == ']':
# rewind to the start of the loop
loop_level = 1
while loop_level > 0:
pc -= 1
if chr(self.tape[pc]) == '[': loop_level -= 1
elif chr(self.tape[pc]) == ']': loop_level += 1
pc -= 1
pc += 1
# DEBUG
#print(pc, self.tape.data.find(b'.'))
def main():
# Working "Hello, World!" program.
#data = bytearray(b'<[.<]>>>>>>>>+\x00!dlroW ,olleH')
# Should print a period, but doesn't.
data = bytearray(b'>++++++++++++++++++++++++++++++++++++++++++++++')
intr = Interpreter(data)
intr.call()
#print(intr.tape.data.decode('ascii').strip('\0'))
if __name__ == "__main__":
main()
The problem:
This line is how I set the program (so I can run this on Ideone.com):
data = bytearray(b'++++++++++++++++++++++++++++++++++++++++++++++')
The program adds to the cell until it is 46, which is the decimal value for an ASCII ., which should print the current cell (a period). But for some reason, the program counter pc never gets to that cell. I want the program to run all code it finds until it hits the end of the tape, but I'm having a hard time getting the program counter to take into account the center of the tape, and ensure that it's still correct if the tape is resized in __setitem__.
The relevant line is (what I was trying out):
while -len(self.tape) <= pc < 0:
which was originally this:
while pc < 0:
So I think that the while line either needs to be adjusted, or I need to change it to while True: and just use a try/except while getting chr(self.tape[pc]) to determine if I've hit the end of the tape.
Does anyone see what is wrong or how to fix it?
Found a solution thanks to Sp3000.
self.end = 0 in Tape.__init__, self.end = max(self.end, index+1) in Tape.__setitem__ and replace the while in Interpreter.call with while pc < self.tape.end:.

Python Threading Issue when starting multiple threads

I would like to start 4 independent threads which are basically methods of a class.
What am I doing wrong in the code below:
from threading import Thread
import time
import random
class Creature:
def __init__(self, name):
self.name = name
def melee(self, level):
self.melee = level
def shielding(self, level):
self.shielding = level
def health(self, total):
self.health = total
def attack(self, attacker, opponent):
while 0 != 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:
print(opponent.name, " is dead")
print("Opponent's health ", opponent.health)
quit()
else:
print(attacker.name, " delivered an attack of ", resistance)
print("Opponent's health ", opponent.health)
def healing(self, healed):
while 0 != 1:
if healed.health <= 0:
if healed.health < 50:
life = random.randint(1, 50)
healed.health += life
if healed.health > 100:
healed.health = 100
print(healed.name, " has healed and now has a health of ", self.health)
Monster = Creature("Wasp")
Monster.health = 100
Monster.melee = 30
Monster.shielding = 15
Player = Creature("Knight")
Player.health = 100
Player.melee = 25
Player.shielding = 20
t1 = Thread(target=Player.attack(Monster, Player))
t1.start()
t2 = Thread(target=Monster.attack(Player, Monster),)
t2.start()
t3 = Thread(target=Player.healing(Player), args=())
t3.start()
t4 = Thread(target=Monster.healing(Monster), args=())
t4.start()
Somehow I am doing something wrong since only the t1 gets started.
Why does the program start only t1? :-(
Thanks!
The problem(s) in your code are all demonstrable in this one line:
t1 = Thread(target=Player.attack(Monster, Player))
The trouble is that you're calling Player.attack, not passing it to the Thread constructor. Your attack method never returns, so you never get past the attempt to create the first Thread. You'd want to do something like this:
t1 = Thread(target = Player.attack, args = (Monster, Player))

Closures in Python - an example [duplicate]

This question already has answers here:
Read/Write Python Closures
(8 answers)
Closed 9 years ago.
I'm performing an action many times in a loop and want to know how far along I am. I'm trying to make a progress report function that should act something like this:
def make_progress_report(n):
i = 0
def progress_report():
i = i + 1
if i % n == 0:
print i
return progress_report
pr = make_progress_report(2)
pr()
pr() # 2
pr()
pr() # 4
This code does not work. Specifically, I get an UnboundLocalError for i. How should I modify it so that it works?
Here are 3 options:
use a list for your counter:
def make_progress_report(n):
i = [0]
def progress_report():
i[0] = i[0] + 1
if i[0] % n == 0:
print i[0]
return progress_report
use itertools.count to track your counter:
from itertools import count
def make_progress_report(n):
i = count(1)
def progress_report():
cur = i.next()
if cur % n == 0:
print cur
return progress_report
Use nonlocal for your counter (Python 3+ only!):
def make_progress_report(n):
i = 0
def progress_report():
nonlocal i
i = i + 1
if i % n == 0:
print i
return progress_report
You could consider using a generator:
def progress_report(n):
i = 0
while 1:
i = i+1
if i % n == 0:
print i
yield # continue from here next time
pr = progress_report(2)
next(pr)
next(pr)
next(pr)
next(pr)
So the progress_report isn't closed over the variable i. You can check it like so...
>>> def make_progress_report(n):
... i=0
... def progress_report():
... i += 1
... if i % n == 0:
... print i
... return progress_report
...
>>> pr = make_progress_report(2)
>>> pr.__closure__
(<cell at 0x1004a5be8: int object at 0x100311ae0>,)
>>> pr.__closure__[0].cell_contents
2
You'll notice there is only one item in the closure of pr. That is the value you passed in originally for n. The i value isn't part of the closure, so outside of the function definition, i is no longer in scope.
Here is a nice discussion on closures in Python: http://www.shutupandship.com/2012/01/python-closures-explained.html
Look again at how you're defining your closure. The n should be passed in when you define the closure... take the following example:
#!/usr/env python
def progressReportGenerator(n):
def returnProgress(x):
if x%n == 0:
print "progress: %i" % x
return returnProgress
complete = False
i = 0 # counter
n = 2 # we want a progress report every 2 steps
getProgress = progressReportGenerator(n)
while not complete:
i+=1 # increment step counter
# your task code goes here..
getProgress(i) # how are we going?
if i == 20: # stop at some arbtirary point...
complete = True
print "task completed"

My state machine in Python runs into infinite loop

I am new to python. My code runs into infinite loop, keeps adding & printing numberCycles. My logic in C++ works fine.
Would you please help to identify why?
The first line of input is the number of simulations.
The next line is the number of minutes for a single reproductive cycle.
The next line is the number of rows (x), followed by a single space, and followed by number of columns (y).
The next group of y lines will have x number of characters, with a single period (.) representing a blank space and a single capitol B representing a starting Bunny tries to reproduce to up, right, down, left direction. If there is a existing bunny, then it goes to sleep.
Input.txt
2 # 2 simulations
5 # 5 minutes/cycle
3 3 # 3*3 map
...
.B.
...
1
4 4
B.BB
..B.
...
B.B
Spot.py
#!/usr/bin/env python
class Spot(object):
isBunny = bool()
nextCycle = 0
UP = 0
RIGHT = 1
DOWN = 2
LEFT = 3
SLEEP = 4
def __init__(self, newIsBunny):
self.isBunny = newIsBunny
self.nextCycle = self.UP
def setNextCycle(self):
if (self.nextCycle != self.SLEEP):
self.nextCycle += 1
def getNextCycle(self):
return self.nextCycle
def getIsBunny(self):
return self.isBunny
def makeBunny(self):
if not self.isBunny:
self.nextCycle = self.UP
self.isBunny = True
Bunny.py
#!/usr/bin/env python
from Spot import Spot
import os
class Bunny(object):
#classmethod
def main(cls, args):
with open(os.path.expanduser('~/Desktop/input.txt')) as f:
numSims = int(f.readline())
myMap = []
print numSims
minPerCycle= int(f.readline())
print minPerCycle
for k in xrange(numSims):
xyLine= f.readline()
row = int(xyLine.split()[0])
col = int(xyLine.split()[1])
print row,col
for i in range(0,row):
myLine = f.readline()
myMap.append([])
for j in range(0,col):
myMap[i].append(Spot(myLine[j] == 'B'))
numCycles = 1
if cls.isFilled(row,col,myMap):
numCycles = 0
while not cls.isFilled(row,col,myMap):
numCycles += 1
print numCycles
for m in range(0,row):
for n in range(0,col):
if myMap[m][n].getIsBunny():
if myMap[m][n].getNextCycle() == Spot.UP:
if m>0:
myMap[m-1][n].makeBunny()
break
elif myMap[m][n].getNextCycle() == Spot.RIGHT:
if n<col-1:
myMap[m][n+1].makeBunny()
break
elif myMap[m][n].getNextCycle() == Spot.DOWN:
if m<row-1:
myMap[m+ 1][n].makeBunny()
break
elif myMap[m][n].getNextCycle() == Spot.SLEEP:
if n>0:
myMap[m][n- 1].makeBunny()
break
myMap[m][n].setNextCycle()
time = numCycles * minPerCycle
print "It took " , time , " minutes for the bunnies to take over the world!\n"
del myMap[:]
f.close()
#classmethod
def isFilled(cls,row,col,myMap):
for a in range(0,row):
for b in range(0,col):
if not myMap[a][b].getIsBunny():
return False
return True
if __name__ == '__main__':
import sys
Bunny.main(sys.argv)
It never goes to the left. Also, you set a break for the inner loop, so, myMap[m][n].setNextCycle() is never called. I just saw your C++ code and you translated it to Python with errors (using Spot.SLEEP instead of Spot.LEFT).
The break statement makes sense in C++ because you want to break the switch. Here, you are using if..else.
It should be something like:
while not cls.isFilled(row, col, myMap):
numCycles += 1
print numCycles
for m in range(0,row):
for n in range(0,col):
if myMap[m][n].getIsBunny():
if myMap[m][n].getNextCycle() == Spot.UP:
if m>0:
myMap[m-1][n].makeBunny()
elif myMap[m][n].getNextCycle() == Spot.RIGHT:
if n<col-1:
myMap[m][n+1].makeBunny()
elif myMap[m][n].getNextCycle() == Spot.DOWN:
if m<row-1:
myMap[m+ 1][n].makeBunny()
elif myMap[m][n].getNextCycle() == Spot.LEFT:
if n>0:
myMap[m][n-1].makeBunny()
myMap[m][n].setNextCycle()

Categories

Resources