I'm struggling to understand this example from a PyCon talk (link to code example)
import threading, time, random
##########################################################################################
# Fuzzing is a technique for amplifying race condition errors to make them more visible
FUZZ = True
def fuzz():
if FUZZ:
time.sleep(random.random())
###########################################################################################
counter = 0
def worker():
'My job is to increment the counter and print the current count'
global counter
fuzz()
oldcnt = counter
fuzz()
counter = oldcnt + 1
fuzz()
print('The count is %d' % counter, end='')
fuzz()
print()
fuzz()
print('---------------', end='')
fuzz()
print()
fuzz()
print('Starting up')
fuzz()
for i in range(10):
threading.Thread(target=worker).start()
fuzz()
print('Finishing up')
fuzz()
The above would print something like:
Starting up
The count is 1The count is 2The count is 2The count is 2
---------------The count is 3
---------------The count is 4
---------------
---------------The count is 4
The count is 5------------------------------
Finishing up
The count is 5
------------------------------
The count is 6---------------
---------------
What I don't understand about this is why sometimes, when a thread is put to sleep it continues from where it left off and other times it stops and starts the next iteration.
EDIT:
I clearly missed the for loop...
All the threads run to completion. That's the point of the lesson. When multiple threads access the same variable simultaneously, you can get unexpected results.
I've modified the code slightly to print less distracting stuff and also a thread id. I think this should help clarify what is happening:
import threading, time, random
##########################################################################################
# Fuzzing is a technique for amplifying race condition errors to make them more visible
FUZZ = True
def fuzz():
if FUZZ:
delay = random.random()
time.sleep(delay)
return delay
###########################################################################################
counter = 0
def worker(id):
'My job is to increment the counter and print the current count'
global counter
fuzz()
oldcnt = counter
delay = fuzz()
counter = oldcnt + 1
print('%d> After %s seconds, the count is %d -> %d' % (id, delay, oldcnt, counter))
print('Starting up')
fuzz()
for i in range(10):
threading.Thread(target=worker, args=(i,)).start()
fuzz()
print('Finishing up')
fuzz()
Example output (because of the randomness, this will change each run):
Starting up
0> After 0.7350378311578315 seconds, the count is 0 -> 1
2> After 0.1785671063273092 seconds, the count is 1 -> 2
1> After 0.8294767498326181 seconds, the count is 0 -> 2
3> After 0.5901283670333175 seconds, the count is 1 -> 3
4> After 0.14964432977752684 seconds, the count is 2 -> 3
7> After 0.07459495416247286 seconds, the count is 2 -> 3
5> After 0.24680028896240813 seconds, the count is 1 -> 2
8> After 0.18685884099452443 seconds, the count is 3 -> 4
Finishing up
6> After 0.8512297712462166 seconds, the count is 2 -> 4
9> After 0.5959592400151894 seconds, the count is 4 -> 5
Because of the random sleep times between when the variable is stored (oldcnt = counter) and when it is incremented (counter = oldcnt + 1) you get the data race.
Related
When using threading with random.shuffle get wrong result from a list.
There are values missing and other duplicated.
Tried use numpy but get the same problem.
#!/usr/bin/python3 -tt
import random
from threading import Thread
class Th(Thread):
def __init__ (self, num, c):
Thread.__init__(self)
self.c = c
self.num = num
def run(self):
random.shuffle(self.c)
for self.i in self.c:
print (str(self.num) + ' - ' + self.i)
def main():
with open ('file.txt', 'rU') as cmddb:
c = [ cmd.strip() for cmd in cmddb]
cmddb.close()
print ('ref:')
for i in c:
print (i)
print ('----')
thread_number=0
for i in range(0,5):
thread = Th(thread_number,c)
thread.start()
thread_number += 1
if __name__ == '__main__':
main()
#################################3
Result:
file.txt:
ref:
10
20
----
0 - 20 <========
1 - 10
1 - 20
0 - 20 <========= duplicated / missimg 10
3 - 20 <=
3 - 20 <= again
4 - 10
2 - 10
4 - 20
2 - 20
Problem occur some times. Other times it run ok.
This happens because the list c is the same list for all threads. So in some cases, some thread prints the first element of c, for example 10, and before it prints the second element, another thread called shuffle on c. if the order of the values in c has changed, the second element is now 10, so it will be printed again. To avoid it, give every thread a different copy of c:
change: thread = Th(thread_number,c) to thread = Th(thread_number, c[:])
I have solved euler problem 12, but it needs some optimization. I have read on the euler forums, but it has not helped me optimized it. However, I have managed to get the solution, I just need to speed it up. It currently takes 4 minutes to run. Here is my code:
import time
def nthtriangle(n):
return (n * (n + 1)) / 2
def numberofnfactors(n):
count = 0
if n==1:
return 1
for i in range(1, 1 + int(n ** 0.5)):
if n % i == 0:
count += 2
return count
def FirstTriangleNumberWithoverxdivisors(divisors):
found = False
counter = 1
while not found:
print(int(nthtriangle(counter)), " ", numberofnfactors(nthtriangle(counter)))
if numberofnfactors(nthtriangle(counter)) > divisors:
print(" first triangle with over ",divisors, " divisors is ", int(nthtriangle(counter)))
found = True
break
counter += 1
start_time = time.time()
FirstTriangleNumberWithoverxdivisors(500)
print("My program took", time.time() - start_time, "to run")
Instead of calculating each triangle number individually, use a generator to get the triangle numbers
from timeit import timeit
def triangle_numbers():
count = 1
num = 0
while True:
num += count
count += 1
yield num
def count_divisors(n):
count = 0
if n==1:
return 1
for i in range(1, 1 + int(n ** 0.5)):
if n % i == 0:
count += 2
return count
print(timeit('next(num for num in triangle_numbers() if count_divisors(num) >= 500)',
globals=globals(), number=1))
Gives me 3.8404819999996107 (seconds) on my machine. You could probably also improve the divisor counting.
What's really slowing you down is calling nthtriangle and numberoffactors more than once in your loop! Plus, those calls to print aren't free.
In the code I made a spreadsheet that records two random numbers every 10 seconds and add the numbers to three rows and when a certain condition was met it could end the loop but then I made it so that after the loop ends a new spreadsheet was created and the loop starts again. However when the loop starts again it adds extra rows with numbers to the sheet when I just want it to end at the same condition the first sheet ended at. I could not seem to figure out why I was getting extra rows filled with numbers
# global variable
globvar = 1
# start generate_random_number function
def generate_random_number():
int1 = random.randint(55, 99)
int2 = random.randint(1, 50)
print('Numbers have been generated')
return int1, int2
# end generate_random_number function
# start create_excel function
def create_excel():
# call global
global globvar
#check file created and not
if(globvar == 1):
wb = Workbook()
wb.remove_sheet(wb.get_sheet_by_name('Sheet'))
wb.create_sheet('Test '+str(globvar))
ws = wb.get_sheet_by_name('Test '+str(globvar))
ws['A1'] = 'Random 1'
ws['B1'] = 'Random 2'
wb.save('sample.xlsx')
print('Document created')
else:
wb = load_workbook('sample.xlsx')
wb.create_sheet('Test '+str(globvar))
ws = wb.get_sheet_by_name('Test '+str(globvar))
ws['A1'] = 'Random 1'
ws['B1'] = 'Random 2'
wb.save('sample.xlsx')
print('Document created')
# end create_excel function
# start update_excel function
def update_excel():
global globvar
wb = load_workbook('sample.xlsx')
ws = wb.get_sheet_by_name('Test '+str(globvar))
ws.append(generate_random_number())
wb.save('sample.xlsx')
print('Spreadsheet updated')
# end update_excel function
# start main function
def main():
global globvar
m = 1
create_excel()
update_excel()
schedule.every(10).seconds.do(update_excel)
loop = True
while loop:
schedule.run_pending()
m += 1
print('Waiting...')
time.sleep(10)
if m == 3:
schedule.run_pending()
loop = False
globvar +=1
#print('Finished program. ' + str(datetime.datetime.now()))
#print('Starting program now.... ' + str(datetime.datetime.now()))
#main()
# end main function
# starts whole script here
print('Starting program now.... '+ str(datetime.datetime.now()))
t = 1
while True:
main()
t += 1
if t == 3:
break
print('Finished program. '+ str(datetime.datetime.now()))
It's because more than 10 seconds have passed between increments of globvar. What will be happening is that you have 2 or more pending calls of update_excel running instead of 1 per increment.
That's not really surprising since you have a schedule of 10 seconds and a 10 second sleep meaning that any extra delays caused by code execution will make the loop take longer than 10 seconds and cause your scheduled tasks to queue.
Instead of using schedule.every just put your update_excel calls directly in your loop.
def main():
...
create_excel()
while loop:
update_excel()
time.sleep(10)
m += 1
if m == 3:
globvar += 1
return
I need to make a stop watch program, I need Start, Stop, Lap, Reset and Quit functions. The program needs print elapsed times whenever the Stop or Lap key is pressed. When the user chooses to quit the program should write a log file containing all the timing data (event and time) acquired during the session in human readable format.
import os
import time
log = ' '
def cls():
os.system('cls')
def logFile(text):
logtime = time.asctime( time.localtime(time.time()) )
f = open('log.txt','w')
f.write('Local current time :', logtime, '\n')
f.write(text, '\n\n')
f.close()
def stopWatch():
import time
p = 50
a = 0
hours = 0
while a < 1:
cls()
for minutes in range(0, 60):
cls()
for seconds in range(0, 60):
time.sleep(1)
cls()
p +=1
print ('Your time is: ', hours, ":" , minutes, ":" , seconds)
print (' H M S')
if p == 50:
break
hours += 1
stopWatch()
I have it ticking the time, however they way I have it wont allow me to stop or lap or take any input. I worked to a few hours to find a way to do it but no luck. Any Ideas on how im going to get the functions working?
I am very new to python and want to write a program that counts down. It has to start at 100 and ends at 1 or 0. How do i have to do that?
This is what i've got now:
def countdown(n):
while n > 0:
print (n)
n = n =2**123
print('Blastoff')
countdown(200)
n = n =2**123
??? What is this supposed to do? What are you trying to accomplish by setting n to 2 to the 123th power? I think
n = n - 1
or
n -= 1
would be more appropriate.
Here is the code:
#!/usr/bin/python
def countdown(count):
while (count >= 0):
print ('The count is: ', count)
count -= 1
countdown(10)
print ("Good bye!")
If you want to it count down in the terms of actual seconds, which I'm going to guess is what you are going for it would be done by causing the countdown to sleep for 1 second at each iteration:
#!/usr/bin/python
import time
def countdown(count):
while (count >= 0):
print ('The count is: ', count)
count -= 1
time.sleep(1)
countdown(10)
print ("Good bye!")
The output is:
The count is: 10
The count is: 9
The count is: 8
The count is: 7
The count is: 6
The count is: 5
The count is: 4
The count is: 3
The count is: 2
The count is: 1
The count is: 0
Good bye!
Let me know if you have any questions.
In python, the indentation matters. All of the lines inside the function have to be indented.
Your method should have been:
def countdown(n):
while n > 0:
print (n)
n = n-1
print("Blastoff")
or a more pythonic way could be:
def countdown(n):
for i in reversed( range(n) ):
print i
print "Blastoff"
Easy way is using range with negative increment parameter.
For example:
for n in range(10,0,-1):
print(n)
Another way: you can use yield command. It's using for making a generator. It's like to return command.
For example:
#this is generator function
def countdown(start,last):
n=start
while(n>last):
yield n
n-=1
for n in countdown(10,0):
print(n)
i would simply wright something like:
import time
num1 = 100
num2 = 0
while (num1 > num2):
print num1
num1 = num1 - 1
time.sleep(1)
Hay Try This You Can Input The Number You Need To Count Down From:
import time
numTimes = int(input("How Many Seconds Do You Wish To Have Untill LiftOff: "))
def countdown(count):
while (count >= 0):
print ("LiftOff In: ", count)
count -= 1
time.sleep(1)
countdown(numTimes)
print ("!WE HAVE LIFT OFF!")
The following code should work:
import time
start = int(input("How many seconds do you want?\nSeconds: "))
for i in range(start,-1,-1):
time.sleep(1)
print(i)
print "Countdown finish!"