简体   繁体   中英

Parallel while Loops in Python

I'm pretty new to Python, and programming in general and I'm creating a virtual pet style game for my little sister.

Is it possible to run 2 while loops parallel to each other in python? eg:

while 1:
    input_event_1 = gui.buttonbox(
        msg = 'Hello, what would you like to do with your Potato Head?',
        title = 'Main Screen',
        choices = ('Check Stats', 'Feed', 'Exercise', 'Teach', 'Play', 'Go to Doctor', 'Sleep', 'Change Favourite Thing', 'Get New Toy', 'Quit'))
    if input_event_1 == 'Check Stats':
        myPotatoHead.check_p_h_stats()
    elif input_event_1 == 'Feed':
        myPotatoHead.feed_potato_head()
    elif input_event_1 == 'Exercise':
        myPotatoHead.exercise_potato_head()
    elif input_event_1 == 'Teach':
        myPotatoHead.teach_potato_head(myPotatoHead)
    elif input_event_1 == 'Play':
        myPotatoHead.play_with_toy()
    elif input_event_1 == 'Sleep':
        myPotatoHead.put_p_h_asleep()
    elif input_event_1 == 'Go to Doctor':
        myPotatoHead.doctor_check_up()
    elif input_event_1 == 'Change Favourite Thing':
        myPotatoHead.change_favourite_thing()
    elif input_event_1 == 'Quit':
        input_quit = gui.ynbox(
            msg = 'Are you sure you want to quit?',
            title = 'Confirm quit',
            choices = ('Quit', 'Cancel'))
        if input_quit == 1:
            sys.exit(0)

while 1:
    time.sleep(20)
    myPotatoHead.hunger = str(float(myPotatoHead.hunger) + 1.0)
    myPotatoHead.happiness = str(float(myPotatoHead.happiness) - 1.0)
    myPotatoHead.tiredness = str(float(myPotatoHead.tiredness) + 1.0)

If not, is there some way that I can turn this into one loop? I want the stuff in the second loop to happen every 20 seconds, but the stuff in the first loop to by constantly happening.

Thanks for any help

Have a look at Threading.Timer .

There is a code recipe here to schedule a function to run every 5 seconds .

import thread
import threading

class Operation(threading._Timer):
    def __init__(self, *args, **kwargs):
        threading._Timer.__init__(self, *args, **kwargs)
        self.setDaemon(True)

    def run(self):
        while True:
            self.finished.clear()
            self.finished.wait(self.interval)
            if not self.finished.isSet():
                self.function(*self.args, **self.kwargs)
            else:
                return
            self.finished.set()

class Manager(object):

    ops = []

    def add_operation(self, operation, interval, args=[], kwargs={}):
        op = Operation(interval, operation, args, kwargs)
        self.ops.append(op)
        thread.start_new_thread(op.run, ())

    def stop(self):
        for op in self.ops:
            op.cancel()
        self._event.set()

if __name__ == '__main__':
    # Print "Hello World!" every 5 seconds

    import time

    def hello():
        print "Hello World!"

    timer = Manager()
    timer.add_operation(hello, 5)

    while True:
        time.sleep(.1)

The only way to "have two while loops in parallel" would be to place them on different threads, but then you need to tackle the synchronization and coordination problems between them since they're reaching into the same object.

I suggest you instead put a time check in the first (and single) loop and perform the increases that you now have in the second loop proportionately to that time-check; not quite satisfactory since the buttonbox call might take an indefinite amount of time to return, but way simpler to arrange, esp. for a beginner, than proper threading coordination.

Once you do have the basic logic in place and working, then you can consider threads again (with a periodic timer for what you'd like in the 2nd loop in one thread, the blocking buttonbox call in the main thread [[I think in easygui it has to be]], both feeding events into a Queue.Queue [[intrinsically thread-safe]] with another thread getting them and operating accordingly, ie most of what you now have in the 1st loop). But that's quite an advanced architectural problem, which is why I recommend you don't try to deal w/it right now!-)

You should use State Machines for this (see the Apress pygame book - downloads here: http://apress.com/book/downloadfile/3765 ), see chapter 7.

A simplified state machine:

def do_play(pet, time_passed):
    pet.happiness += time_pass*4.0

def do_feed(pet, time_passed):
    pet.hunger -= time_passed*4.0

def do_sleep(pet, time_passed):
    pet.tiredness += time_passed*4.0
    if pet.tiredness <= 0:
        return 'Waiting'

def do_waiting(pet, time_passed):
    pass

def do_howl(pet, time_passed):
    print 'Hoooowl'

def do_beg(pet, time_passed):
    print "I'm bored!"

def do_dead(pet, time_passed):
    print '...'

STATE_TO_FUNC = dict(Waiting=do_waiting,
                     Sleeping=do_sleep,
                     Feeding=do_feed,
                     Playing=do_play,
                     Howling=do_howl,
                     Begging=do_beg,
                     Dead=do_dead
                     )

class Pet:
    def __init__(self):
        self.state = 'Waiting'
        self.hunger = 1.0
        self.tiredness = 1.0
        self.happiness = 1.0

    def process(self, time_passed):
        self.hunger +=1*time_passed
        self.tiredness +=1*time_passed
        self.happiness -= 1*time_passed

        func = STATE_TO_FUNC[self.state]
        new_state = func(self, time_passed)
        if new_state is not None:
            self.set_state(new_state)

        if self.hunger >10:
            self.set_state('Dead')
        elif self.hunger > 5 and not (self.state == 'Feeding'):
            self.set_state('Howling')
        elif self.tiredness > 5:
            self.set_state('Sleeping')
        elif self.happiness < 0 and not (self.state == 'Playing'):
            self.set_state('Begging')

    def set_state(self,state):
        if not self.state == 'Dead':
            self.state = state

from msvcrt import getch
import time
pet = Pet()
while True:
    time0 = time.time()
    cmd = getch() # what command?
    pet.process(time.time()-time0)
    if cmd == 'a':
        pet.set_state('Feeding')
    if cmd == 's':
        pet.set_state('Sleeping')
    if cmd == 'd':
        pet.set_state('Playing')

put one of them into a function, the threading.Thread class supports a target attribute:

import threading
threading.Thread(target=yourFunc).start()

Will start yourFunc() running in the background.

Essentially to have processing to happen in parallel you have several solutions

1- Separate processes (ie: programs) running independently that speak to one another through a specific protocol (eg: Sockets)

2- Or you can have the one process spawn off multiple threads

3- Build an event queue internally and process them one by one

That is the general picture.

As for the specific answer to your question, you said "the stuff in the first loop to b[e] constantly happening". The reality is you never want this to happen all the time, because all that will do is use up 100% of the CPU and nothing else will ever get done

The simplest solution is probably number 3.

The way I would implement it is in my main loop have a thread that goes through an event queue and sets a timer for each event. Once all the timers have been sent the main loop then goes to sleep.

When a timer times out, an other function will then run the corresponding function for the event that triggered that timer.

In your case, you have two events. One for displaying the selection menu (first loop) and the second for changing myPotatoHead. The timer associated with the first one, I would set to 0.5sec, making it larger reduces CPU usage but slows down responsivness, increasing it usses up more CPU, for the second event I would set a 20 second timer.

Ofcourse when the timer expires, you would not do while 1 but you will just go through your while loop body once (ie get rid of while).

i think they cannot be coupled in to one while loop. maybe you need to check the threading or multiprocessing library.

There is also a package called SimPy that you could also look at. The and libraries may also help. 库也可能有所帮助。

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM