2

I'm writing a bot in python and to "humanize" it I need to pause and resume functions randomly. Functions can be paused and resumed only at some defined points.

The bot is made of various functions, like

do_action1(*args)
do_action2(*args)
do_action3(*args)
...
start_bot()
stop_bot()

The function start_bot() calls do_action1(), do_action2(), ... in order and gives them *args.
I need to find a way to start a do_actionX() function randomly and at some points pause it and run another random do_actionX() function then pause it and resume the previous one and so on...

To start a function randomly I thought I can use a dictionary with functions inside and pick one of them randomly.

I think I can do this with threads, but since my bot is using multiprocessing, would it be a right choice to use multithreading and multiprocessing together?
I use multiprocessing to run multiple bots at the same time and manage them from a main Python script which is linked to an interface. Each bot instance connects to a different account.

If I use multithreading, how can I make the function stop at some defined points and not randomly?

For example:

def do_action1(*args):
     print("something")
     # do something else
     # <--- at this point the function could be paused
     print("something")
     # <--- at this pint the function cannot be paused!
     print("else")
     # <--- and here the function could be paused again

The times that a function will be paused must be random. Is there a way to do this?
Are threads the right approach to this issue?

SiHa
  • 7,830
  • 13
  • 34
  • 43
  • 3
    Why not use yield inside the function to pause? And recall the same function if no other function needs to run. This would make it one thread per bot – user1827356 Dec 02 '15 at 20:34
  • 1
    See [A Curious Course on Coroutines and Concurrency](http://www.dabeaz.com/coroutines/) – Peter Wood Dec 02 '15 at 20:42

2 Answers2

2

You could use the yield keyword to create co-routines. Here is one example:

import random
random.seed()

def do_action1():
    print("Hello")
    yield
    print("HELLO!")
    yield
    print("hello?")
def do_action2():
    print("Are you there?")
    yield
    print("ARE YOU THERE!")
    yield
    print("I'm scared.")
def do_action3():
    print("Is somebody out there?")
    yield
    print("SOMEBODY IS OUT THERE!")
    yield
    print("I'm dead.")

def bot(*actions):
    actions = [action() for action in actions]
    while actions:
        action = random.choice(actions)
        try:
            next(action)
        except StopIteration:
            actions.remove(action)
    return

bot(do_action1, do_action2, do_action3)
Robᵩ
  • 163,533
  • 20
  • 239
  • 308
0

Threads are one good way to do it. To pause the function, use time.sleep(), (based on How can I make a time delay in Python?):

import time, random

# Time range to sleep, in seconds
def randomWait():
    sleepMin = 1.0
    sleepMax = 5.0
    time.sleep(random.uniform(sleepMin, sleepMax))

def do_action1(*args):
    print("something")
    # do something else
    randomWait()
    print("something")
    print("else")
    randomWait()

I used random.uniform in this example, but you are free to make the randomness of the interval as fancy as you would like using the random module or anything else you like.

A threaded solution will allow different actions to run simultaneouly. If you do not want to allow this, use a single thread and use yield as user1827356 suggests:

import time, random

# Time range to sleep, in seconds
def randomWait():
    sleepMin = 1.0
    sleepMax = 5.0
    time.sleep(random.uniform(sleepMin, sleepMax))

def do_action1(*args):
    print("something")
    # do something else
    yield
    print("something")
    print("else")

# Other actions defined similarly

actions = [do_action1(), do_action2(), do_action3()]
while actions:
    randomWait()
    action = random.choice(actions)
    try:
        next(action)
    except StopIteration:
        actions.remove(action)

Note that in this case your actions are generators. They are basically objects that run the code you define and store the state (pause) whenever they hit a yield keyword. For a single-threaded application, this is probably the best way to go.

Community
  • 1
  • 1
Mad Physicist
  • 107,652
  • 25
  • 181
  • 264
  • You've not copied @Robᵩ's answer correctly. You don't called the actions so they never get to the first `yield`. – Peter Wood Dec 02 '15 at 21:29
  • Thanks for the catch. I did not copy Rob although he did post that part first. My answer had the StopIteration catch before his was edited. – Mad Physicist Dec 02 '15 at 21:31