I have a working solution for the Sleeping Barber operating system problem using python 2.7 and threading that works with a single barber and a certain amount of chairs. But I would like it to be able to work in a situation where there are multiple barbers, in the same way as there is multiple customers.
Here is my current solution with a single barber:
from threading import Thread, Lock, Event
import time, random
from sys import exit
lock = Lock()
customerIntervalMin = 5
customerIntervalMax = 15
haircutDurationMin = 3
haircutDurationMax = 15
class BarberShop:
waitingCustomers = []
threads=[]
finishedCustomers = []
def __init__(self, barber, numberOfSeats):
self.barber = barber
self.numberOfSeats = numberOfSeats
def openShop(self):
print 'Barber shop is opening'
workingThread = Thread(target = self.barberGoToWork)
workingThread.start()
self.threads.append(workingThread)
def barberGoToWork(self):
while True:
lock.acquire()
if len(self.waitingCustomers) > 0 and len(self.finishedCustomers)< 5:
c = self.waitingCustomers[0]
del self.waitingCustomers[0]
lock.release()
self.barber.cutHair(c)
self.finishedCustomers.append(c)
elif len(self.waitingCustomers)==0 and len(self.finishedCustomers)<5:
lock.release()
print 'Aaah, all done, {0} is going to sleep'.format(barber.name)
barber.sleeps()
print '{0} woke up'.format(barber.name)
elif len(self.waitingCustomers)==0 and len(self.finishedCustomers)==5:
lock.release()
print 'The barber shop is closed. Come back tomorrow.'
exit(0)
def enterBarberShop(self, customer):
lock.acquire()
print '{0} entered the shop and is looking for a seat'.format(customer.name)
if len(self.waitingCustomers) == self.numberOfSeats:
print 'Waiting room is full, {0} is leaving.'.format(customer.name)
lock.release()
else:
print '{0} sat down in the waiting room'.format(customer.name)
self.waitingCustomers.append(c)
lock.release()
barber.wakeUp()
class Customer:
def __init__(self, name):
self.name = name
class Barber:
def __init__(self, name):
self.name = name
barberEvent = Event()
def sleeps(self):
self.barberEvent.wait()
def wakeUp(self):
self.barberEvent.set()
def cutHair(self, customer):
self.barberEvent.clear()
print '{0} is having a haircut done by {1}'.format(customer.name, self.name)
randomHairCuttingTime = random.randrange(haircutDurationMin, haircutDurationMax+1)
time.sleep(randomHairCuttingTime)
print '{0} is done with {1}'.format(customer.name, self.name)
if __name__ == '__main__':
customers = []
customers.append(Customer('Ken'))
customers.append(Customer('Scott'))
customers.append(Customer('Larry'))
customers.append(Customer('Liam'))
customers.append(Customer('Kieran'))
barber = Barber('Mark')
barberShop = BarberShop(barber, numberOfSeats=10)
barberShop.openShop()
while len(customers) > 0:
c = customers.pop()
barberShop.enterBarberShop(c)
customerInterval = random.randrange(customerIntervalMin,customerIntervalMax+1)
time.sleep(customerInterval)
I'm confused as to how to go about this. I originally thought it would be the same as the customers list inside the main where you would just append the class & the given name parameter into a list, use a loop to go through the list, pop each instance in the list & assign that to barber & keep the original barberShop definition in the main. But upon reflection that can't be right because that would just create 3 different threads with 10 seats each. So now I am unsure of how to solve the last part of this problem and while there is plenty of online implementations of this specific aspect of the problem in languages like Java & C but I don't have enough experience with those languages to even understand those solutions, let alone translate them into python & implement aspects of it in the solution above.
Is there any other way I can implement multiple barbers into this solution? Any help at all on this aspect of the problem or any suggestions for improvements that can be made to my solution would be greatly appreciated.
You need to design your system exactly as you've described in words: you instantiate three barbers, each of whom has full permissions on the same ten-chair waiting room. This means that your mutex operations have to be hardy enough for 3 actors and 10 targets. You also have to allow a new customer to wake up a sleeping barber, regardless of how many are asleep.
Think in terms of your barber states and your customer states. Focus on each combination in turn, and design how the system should react. Then, given those various actions (e.g. "customer wakes up barber", "barber moves customer to work chair"), decide how to allocate your methods to classes.
Does that help you get moving?
Related
How do I change a parameter of a function running in an infinite loop in a thread (python)?
I am new to threading and python but this is what I want to do (simplified),
class myThread (threading.Thread):
def __init__(self, i):
threading.Thread.__init__(self)
def run(i):
self.blink(i)
def blink(i):
if i!=0:
if i==1:
speed=0.10
elif i==2:
speed=0.20
elif i==3:
speed=0.30
while(true):
print("speed\n")
i=3
blinkThread=myThread(i)
blinkThread.start()
while(i!=0):
i=input("Enter 0 to Exit or 1/2/3 to continue\n")
if i!=0:
blinkThread.run(i)
Now, obviously this code gives errors regarding the run() method. I want to run the function blink() in infinite loop but change the 'i' variable. I also cannot do it without a thread because I have other portions of code which are doing parallel tasks. What can I do?
Thanks!
Best thing to learn first, is to never change variables from different threads. Communicate over queues:
import threading
import queue
def drive(speed_queue):
speed = 1
while True:
try:
speed = speed_queue.get(timeout=1)
if speed == 0:
break
except queue.Empty:
pass
print("speed:", speed)
def main():
speed_queue = queue.Queue()
threading.Thread(target=drive, args=(speed_queue,)).start()
while True:
speed = int(input("Enter 0 to Exit or 1/2/3 to continue: "))
speed_queue.put(speed)
if speed == 0:
break
main()
Besides a lot of syntax errors, you're approaching the whole process wrong - there is no point in delegating the work from run to another method, but even if there was, the last while would loop infinitely (if it was actually written as while True:) never checking the speed change.
Also, don't use run() method to interface with your thread - it's a special method that gets called when starting the thread, you should handle your own updates separately.
You should also devote some time to learn OOP in Python as that's not how one makes a class.
Here's an example that does what you want, hope it might help you:
import threading
import time
class MyThread (threading.Thread):
def __init__(self, speed=0.1):
self._speed_cache = 0
self.speed = i
self.lock = threading.RLock()
super(MyThread, self).__init__()
def set_speed(self, speed): # you can use a proper setter if you want
with self.lock:
self.speed = speed
def run(self):
while True:
with self.lock:
if self.speed == 0:
print("Speed dropped to 0, exiting...")
break
# just so we don't continually print the speed, print only on change
if self.speed != self._speed_cache:
print("Current speed: {}".format(self.speed))
self._speed_cache = self.speed
time.sleep(0.1) # let it breathe
try:
input = raw_input # add for Python 2.6+ compatibility
except NameError:
pass
current_speed = 3 # initial speed
blink_thread = MyThread(current_speed)
blink_thread.start()
while current_speed != 0: # main loop until 0 speed is selected
time.sleep(0.1) # wait a little for an update
current_speed = int(input("Enter 0 to Exit or 1/2/3 to continue\n")) # add validation?
blink_thread.set_speed(current_speed)
Also, do note that threading is not executing anything in parallel - it uses GIL to switch between contexts but there are never two threads executing at absolutely the same time. Mutex (lock) in this sense is there just to ensure atomicity of operations, not actual exclusiveness.
If you need something to actually execute in parallel (if you have more than one core, that is), you'll need to use multiprocessing instead.
What I am trying to do is to run or pause two different functions depending on if their respective option button is active or not (the two functions execution is mutually exclusive) I tried using semaphores based on the answer given by Jay here but both threads seem to continue running. Here the code I am writing (it is inside an __init__ block):
self.manual_sema = Semaphore(0)
self.auto_sema = Semaphore(0)
#Function A.
def Exec_Manual():
while True:
for i in range(0,5):
if self.rbtnMan.isChecked():
if self.rbtnAuto.isChecked():
self.manual_sema.acquire()
self.auto_sema.release()
break
self._tx_freq1_line_edit.setEnabled(1)
self._tx_freq2_line_edit.setEnabled(1)
self._tx_freq3_line_edit.setEnabled(1)
self._tx_freq4_line_edit.setEnabled(1)
self._tx_freq5_line_edit.setEnabled(1)
frec = 'self._tx_freq'+str(i+1)+'_line_edit.text()'
efrec = float(eval(frec))
self.lblTx1.setText(str(efrec-0.4))
self.lblTx2.setText(str(efrec))
self.lblTx3.setText(str(efrec+0.4))
#print frec
print efrec
print threading.currentThread().getName()
for th in threading.enumerate():
print th
time.sleep(1)
manual_thread = threading.Thread(target=Exec_Manual, name='manual_thread')
manual_thread.daemon = True
manual_thread.start()
#Function B.
def Exec_Auto():
while True:
for i in range(0,17):
if self.rbtnAuto.isChecked():
if self.rbtnMan.isChecked():
self.auto_sema.acquire()
self.manual_sema.release()
break
self._tx_freq1_line_edit.setDisabled(1)
self._tx_freq2_line_edit.setDisabled(1)
self._tx_freq3_line_edit.setDisabled(1)
self._tx_freq4_line_edit.setDisabled(1)
self._tx_freq5_line_edit.setDisabled(1)
frec0=88.5
x=i*1.2
efrec = float(frec0)+x
print efrec
print threading.currentThread().getName()#Debug
for th in threading.enumerate():
print th
self.lblTx1.setText(str(efrec-0.4))
self.lblTx2.setText(str(efrec))
self.lblTx3.setText(str(efrec+0.4))
time.sleep(1)
frec0=frec0+1.2
auto_thread = threading.Thread(target=Exec_Auto, name='auto_thread')
auto_thread.daemon = True
auto_thread.start()
Both auto_thread and manual_thread seem to continue executing since threading.enumerate() keeps showing them active (I expect one them to dissapear when the other function's radio button is checked and re start when their own radio button is checked again):
<_MainThread(MainThread, started 140030203459328)>
<Thread(manual_thread, started daemon 140029502768896)>
<Thread(auto_thread, started daemon 140029154293504)>
In the answer given by Jay here, I can see that he is creating classes that inherite from thread and putting the functions inside those classes. ¿may that be what needs to be done to obtain the behavior I'm expecting? ¿can you provide some ideas on how to get my code to work as I expect? as always, thanks in advance.
Regards,
Ron
I think that trying to synchronize threads is more trouble than its worth. You need to consider timing windows such as the GUI changing state between when the two threads happen to check them. It would be easier to take the logic out of the functions and have a higher level function call one or the other on each loop. That function can tell the worker functions whether there is a state change requiring changes to the gui.
def Exec_Manual(state_changed):
if state_changed:
self._tx_freq1_line_edit.setEnabled(1)
self._tx_freq2_line_edit.setEnabled(1)
self._tx_freq3_line_edit.setEnabled(1)
self._tx_freq4_line_edit.setEnabled(1)
self._tx_freq5_line_edit.setEnabled(1)
frec = 'self._tx_freq'+str(i+1)+'_line_edit.text()'
efrec = float(eval(frec))
self.lblTx1.setText(str(efrec-0.4))
self.lblTx2.setText(str(efrec))
self.lblTx3.setText(str(efrec+0.4))
time.sleep(1)
def Exec_Auto(state_changed):
if state_changed:
self._tx_freq1_line_edit.setDisabled(1)
self._tx_freq2_line_edit.setDisabled(1)
self._tx_freq3_line_edit.setDisabled(1)
self._tx_freq4_line_edit.setDisabled(1)
self._tx_freq5_line_edit.setDisabled(1)
frec0=88.5
x=i*1.2
efrec = float(frec0)+x
self.lblTx1.setText(str(efrec-0.4))
self.lblTx2.setText(str(efrec))
self.lblTx3.setText(str(efrec+0.4))
time.sleep(1)
def the_decider():
state = self.rbtnAuto.isChecked()
while True:
new_state = self.rbtnAuto.isChecked()
if new_state:
Exec_Auto(state == new_state)
else:
Exec_Manual(state == new_state)
state = new_state
auto_thread = threading.Thread(target=the_decider, name='the_decider')
auto_thread.start()
I have an application that fires up a series of threads. Occassionally, one of these threads dies (usually due to a network problem). How can I properly detect a thread crash and restart just that thread? Here is example code:
import random
import threading
import time
class MyThread(threading.Thread):
def __init__(self, pass_value):
super(MyThread, self).__init__()
self.running = False
self.value = pass_value
def run(self):
self.running = True
while self.running:
time.sleep(0.25)
rand = random.randint(0,10)
print threading.current_thread().name, rand, self.value
if rand == 4:
raise ValueError('Returned 4!')
if __name__ == '__main__':
group1 = []
group2 = []
for g in range(4):
group1.append(MyThread(g))
group2.append(MyThread(g+20))
for m in group1:
m.start()
print "Now start second wave..."
for p in group2:
p.start()
In this example, I start 4 threads then I start 4 more threads. Each thread randomly generates an int between 0 and 10. If that int is 4, it raises an exception. Notice that I don't join the threads. I want both group1 and group2 list of threads to be running. I found that if I joined the threads it would wait until the thread terminated. My thread is supposed to be a daemon process, thus should rarely (if ever) hit the ValueError Exception this example code is showing and should be running constantly. By joining it, the next set of threads doesn't begin.
How can I detect that a specific thread died and restart just that one thread?
I have attempted the following loop right after my for p in group2 loop.
while True:
# Create a copy of our groups to iterate over,
# so that we can delete dead threads if needed
for m in group1[:]:
if not m.isAlive():
group1.remove(m)
group1.append(MyThread(1))
for m in group2[:]:
if not m.isAlive():
group2.remove(m)
group2.append(MyThread(500))
time.sleep(5.0)
I took this method from this question.
The problem with this, is that isAlive() seems to always return True, because the threads never restart.
Edit
Would it be more appropriate in this situation to use multiprocessing? I found this tutorial. Is it more appropriate to have separate processes if I am going to need to restart the process? It seems that restarting a thread is difficult.
It was mentioned in the comments that I should check is_active() against the thread. I don't see this mentioned in the documentation, but I do see the isAlive that I am currently using. As I mentioned above, though, this returns True, thus I'm never able to see that a thread as died.
I had a similar issue and stumbled across this question. I found that join takes a timeout argument, and that is_alive will return False once the thread is joined. So my audit for each thread is:
def check_thread_alive(thr):
thr.join(timeout=0.0)
return thr.is_alive()
This detects thread death for me.
You could potentially put in an a try except around where you expect it to crash (if it can be anywhere you can do it around the whole run function) and have an indicator variable which has its status.
So something like the following:
class MyThread(threading.Thread):
def __init__(self, pass_value):
super(MyThread, self).__init__()
self.running = False
self.value = pass_value
self.RUNNING = 0
self.FINISHED_OK = 1
self.STOPPED = 2
self.CRASHED = 3
self.status = self.STOPPED
def run(self):
self.running = True
self.status = self.RUNNING
while self.running:
time.sleep(0.25)
rand = random.randint(0,10)
print threading.current_thread().name, rand, self.value
try:
if rand == 4:
raise ValueError('Returned 4!')
except:
self.status = self.CRASHED
Then you can use your loop:
while True:
# Create a copy of our groups to iterate over,
# so that we can delete dead threads if needed
for m in group1[:]:
if m.status == m.CRASHED:
value = m.value
group1.remove(m)
group1.append(MyThread(value))
for m in group2[:]:
if m.status == m.CRASHED:
value = m.value
group2.remove(m)
group2.append(MyThread(value))
time.sleep(5.0)
I'm writing the code for a robot which my college is entering into a competition. I'm currently trying to build some wheel encoders using reflectance sensors. I realised a while back that I would probably need to use threading to achieve this, seeing as the robot needs to monitor both the left and right encoders at the same time. The code below is what I have so far:
from __future__ import division
import threading
import time
from sr import *
R = Robot()
class Encoder(threading.Thread):
def __init__(self, motor, pin, div=16):
self.motor = motor
self.pin = pin
self.div = div
self.count = 0
threading.Thread.__init__(self)
def run(self):
while True:
wait_for(R.io[0].input[self.pin].query.d)
self.count += 1
def rotations(self, angle, start_speed=50):
seg = 360/self.div
startcount = self.count
current_dist = angle #Distance away from target
R.motors[self.motor].target = start_speed
while current_dist > 360:
newcount = self.count - startcount
current_dist = angle - newcount*seg
R.motors[self.motor].target = 50
while abs(current_dist) > seg/2:
newcount = self.count - startcount
current_dist = angle - newcount*seg
current_speed = start_speed * current_dist / 360
if current_speed < 5:
R.motors[self.motor].target = 5
else:
R.motors[self.motor].target = current_speed
R.motors[self.motor].target = 0
WheelLeft = Encoder(0,0)
WheelLeft.start()
WheelRight = Encoder(1,3)
WheelRight.start()
WheelRight.rotations(720)
WheelLeft.rotations(720)
The sr module is provided by Southampton University, who are running the competition. It allows us to interact with the robot's hardware.
Now, the threads which get created seem to allow the two reflectance sensors to be monitored separately. This bit of code: R.io[0].input[self.pin].query.d works out whether the value coming from the reflectance sensor has changed. The 'rotations' method turns the wheel through a certain angle by constantly checking how many degrees the wheel has already turned through, and slowing it down as it reaches the end. I would like both wheels to start turning when I run the program, and then slow down and stop when they have gone through 2 rotations. Currently though, when I run the program, one wheel starts turning and slows down and stops, followed by the other wheel. It seems to me like the 'rotations' method is not running in a thread, like the 'run' method is. Is it only the code under the 'run' method that runs in a thread, or is it the whole class?
If it helps, I've been following this tutorial: http://www.devshed.com/c/a/Python/Basic-Threading-in-Python/1/
Also, I would like to know why it is possible to start a thread only with Encoder(0,0).start(). Why do you not have to create an object using the class (e.g. Thread = Encoder(0,0).start() for a new thread to be created?
Sorry if the terminoligy I've used isn't up to scratch, as you can probably tell I'm quite new to threading, and programming in general.
Encoder(0,0).start() is a call to the method to start the thread. In turn, this method calls your run implementation, which doesn't use the rotations method. If you want to do so, then you have to call it in the while loop of run.
With Thread = Encoder(0,0).start() you store the value retrieved from that call (which is None), but to get it you need to start the new thread first anyway.
The run method is the thread of execution.
If you want something else to happen in that thread, you have to call it from Encoder.run().
Oh, and Encoder(0,0).start() does create an object. Just because you didn't bind that object to a local variable doesn't mean it doesn't exist. If it didn't exist you couldn't call its start method.
You have to be very careful about its lifetime though, without a local variable keeping it alive.
You can extend from SR's Poll class so that it can be used in a wait_for:
import poll
class Encoder(poll.Poll):
def __init__(self, motor, pin, div=16):
self.motor = motor
self.pin = pin
self.div = div
self.count = 0
self.target_reached = False
# kick off a thread to count the encoder ticks
self.counter_thread = threading.Thread(target=self._update_count)
self.counter_thread.start()
def _update_count(self):
while True:
wait_for(R.io[0].input[self.pin].query.d)
self.count += 1
def rotations(self, angle, start_speed=50):
if not self.target_reached:
raise Exception("Last motion still in progress!")
self.target_reached = False
# kick off a thread to control the speed
self.angle_thread = threading.Thread(
target=self._update_speeds,
args=(angle, start_speed)
)
self.angle_thread.start()
def _update_speeds(self, angle, start_speed):
# control the motor speed as before
...
# let things know we're done
self.target_reached = True
# implement poll methods
def eval(self):
return (self.target_reached, None)
Which then lets you do:
wheelLeft = Encoder(0,0)
wheelRight = Encoder(1,3)
wheelRight.rotations(720)
wheelLeft.rotations(720)
wait_for(wheelRight & wheelLeft)
Note that an encoder isn't itself a thread - it's a "has a" relationship, not an "is a" relationship
The question may be really stupid but I'm working on this code since this morning and now even stupid things are hard :\
I've got this code and I call it by making 8 processes and run them.
Then there's another thread that has to print infos about this 8 processes. (code is below).
import MSCHAPV2
import threading
import binascii
import multiprocessing
class CrackerThread(multiprocessing.Process):
password_header = "s."
current_pin = ""
username = ""
server_challenge = ""
peer_challenge = ""
nt_response = ""
starting_pin = 0
limit = 0
testing_pin = 0
event = None
def __init__(self, username, server_challenge, peer_challenge, nt_response, starting_pin, limit, event):
#threading.Thread.__init__(self)
super(CrackerThread, self).__init__()
self.username = username
self.server_challenge = server_challenge
self.peer_challenge = peer_challenge
self.nt_response = nt_response
self.starting_pin = starting_pin
self.limit = limit
self.event = event
self.testing_pin = starting_pin
#self.setDaemon(True)
def run(self):
mschap = MSCHAPV2.MSCHAPV2()
pin_range = self.starting_pin+self.limit
while self.testing_pin <= pin_range and not self.event.isSet():
self.current_pin = "%s%08d" % (self.password_header, self.testing_pin)
if(mschap.CheckPassword(self.server_challenge, self.peer_challenge, self.username, self.current_pin.encode("utf-16-le"), self.nt_response)):
self.event.set()
print "Found valid password!"
print "user =", self.username
print "password =", self.current_pin
self.testing_pin+=1
print "Thread for range (%d, %d) ended with no success." % (self.starting_pin, pin_range)
def getCurrentPin(self):
return self.testing_pin
def printCrackingState(threads):
info_string = '''
++++++++++++++++++++++++++++++++++
+ Starting password = s.%08d +
+--------------------------------+
+ Current pin = s.%08d +
++++++++++++++++++++++++++++++++++
+ Missing pins = %08d +
++++++++++++++++++++++++++++++++++
'''
while 1:
for t in threads:
printed_string = info_string % (t.starting_pin, t.getCurrentPin(), t.getMissingPinsCount())
sys.stdout.write(printed_string)
sys.stdout.write("--------------------------------------------------------------------")
time.sleep(30)
printCrackingState is called by these lines in my "main":
infoThread = threading.Thread(target = utils.printCrackingState, args=([processes]))
#infoThread = cursesTest.CursesPrinter(threads, processes, event)
infoThread.setDaemon(True)
infoThread.start()
Now the quesion is: why t.starting_pin and t.getCurrentPin() print the SAME value?
It's like the t.getCurrentPin() returns the value set in the __init__() method and is not aware that I'm incrementing it!
Suggestions?
Your problem here is that you're trying to update a variable in one process, and read it in another process. You can't do that. The whole point of multiprocessing, as opposed to multithreading, is that variables are not shared by default.
Read the docs, especially Exchanging objects between processes and Sharing state between processes, and it will explain the various ways around this. But really, there's two: either you need some kind of channel/API to let the parent process ask the child process for its current state, or you need some kind of shared memory to store the data in. And you may need a lock to protect either the channel/shared memory.
While shared memory may seem like the "obvious" answer here, you may want to time the following:
val = 0
for i in range(10000):
val += 1
val = Value('i', 0)
lock = Lock()
for i in range(10000):
with lock:
val.value += 1
It's worth noting that your code would also be incorrect with threads—although it would probably work, in CPython. If you don't do any synchronization, there is no guaranteed ordering. If you write a value in one thread and read it "later" in another thread, you can still read the older value. How much later? Well, if thread 0 runs on core 0, and thread 1 on core 1, and they both have the variable in their cache, and nobody tells the CPUs to flush the cache, thread 1 will go on reading the old value forever. In practice, CPython's Global Interpreter Lock eventually synchronizes everything implicitly (so we're talking milliseconds rather than infinity), and all variables have explicit memory locations rather than being, say, optimized into registers, and so on, so you can usually get away with writing unprotected races. But, thanks to Murphy's Law, you should read "usually" as "every time until the first demo to the investors" or "until we attach the live nuclear reactor".