Threading module

Hello everyone,

i’m using threading module to execute functions asynchronous.

all functions are wrapped in a while True loop for continous execution.
my issue is that functions are triggered from the while True loop, and executing again even if the previous execution is still in progress.
For example the while True is 0.5 sec and the function needs 4-5 seconds to be done.

How can i achieve something like this ? (Not re-execute the thread.start function if the previous .start() still running ?)

Thanks for any comments.

That does not make sense.

If the thread is doing work then it cannot start doing more work.

Maybe if you share code that shows what you are doing it will be easier to understand what you are doing.

Hello, yes of course.
Below are sample functions and modbus_pulse function taking 4 seconds.
All is executed with threading.start() and everything wrapped in infinite loop.

what i’m seeking is : not re-execute the modbus_pulse function if already there is an execution going on. (is actually executed every time from while True loop).

thanks for any suggestions.

import pymodbus
from pymodbus.client import ModbusTcpClient
from datetime import datetime
import random
import time
import threading


modbusdevice1=ModbusTcpClient('127.0.0.1', port=502)
modbusdevice2=ModbusTcpClient('127.0.0.1', port=502)
modbusdevice3=ModbusTcpClient('127.0.0.1', port=502)

counter=0



def modbus_pulse(ip='127.0.0.1' , port=502, address=10, slaveid=1 , length=3):

    modbusdevice=ModbusTcpClient(ip, port=port)
    state = True
    print(state)
    
    modbusdevice.write_registers(address,0,slaveid)
    time.sleep(0.2)
    modbusdevice.write_registers(address,1,slaveid)
    time.sleep(length)
    modbusdevice.write_registers(address,0,slaveid)
    time.sleep(2)
    state = False
    print(state)
    
    return None


def work1():
    
    modbusdevice1.write_registers(1,random.randint(0,32000),1)
    return None


def work2():
    modbusdevice1.write_registers(2,5,1)
    return None


def work3():
    modbusdevice2.write_registers(3,datetime.now().second,1)
    return None


def work4():
    global counter
    counter=counter+1
    modbusdevice2.write_registers(0,counter,1)
    return None



while True:
    

    wmp=threading.Thread(target=modbus_pulse)
    wmp.start()


    
    threading.Thread(target=work1).start()
    threading.Thread(target=work2).start()
    threading.Thread(target=work3).start()
    threading.Thread(target=work4).start()
    
    time.sleep(0.2)

Moved this to Python Help for more visibility and removed the free-threading tag because it’s not related to PEP 703.

i have tried to check is_alive before executing and now is not working in parallel.

"""
2024 09 25
"""
import time
import threading
from datetime import datetime




def f1():
    print('Hello ... 11');
    time.sleep(2);
    print('Hello ... 12'); 
    time.sleep(2);
    print('Hello .... 13'); 
    time.sleep(2);
    print('Hello .... end 14'); 
    time.sleep(2);
    print('f1 finish')
    

def f2():
    print('........World!..21'); 
    time.sleep(1);
    print('....... World!22'); 
    time.sleep(1);
    print('......World!..23'); 
    time.sleep(1);
    print('....... World!24'); 
    time.sleep(1);
    print('.......World!..25'); 
    time.sleep(1);
    print('........World! end 26'); 
    time.sleep(1);
    print('f2 finish')


while True:


    t1=threading.Thread(target=f1())
    if t1.is_alive()==False:
        t1.run()


    t2=threading.Thread(target=f2())
    if t2.is_alive()==False:
        t2.run()


    time.sleep(0.5)

Result:
Hello … 11
Hello … 12
Hello … 13
Hello … end 14
f1 finish
…World!..21
… World!22
…World!..23
… World!24
…World!..25
…World! end 26
f2 finish
Hello … 11
Hello … 12

try Lock Objects of threading :face_with_raised_eyebrow:

start() and run() are different.

Basically

start() calls run() in a new thread, whereas calling run directly does the thing in the same thread. Which is exactly what you’ve noticed happening.

Swap .run() for .start() in your second example.

Hello David,

triggering like code below is executing new thread every time. Is there any way to check if running not to run again ? even if i used the check function is_alive, is executing new thread every time.


while True:
    
    t1=threading.Thread(target=f1)
    if t1.is_alive()==False:
        t1.start()


    t2=threading.Thread(target=f2)
    if t2.is_alive()==False:
        t2.start()

Oh sorry, I just realised this problem:

t1 is redefined each loop, you’ll need to create the threads outside of the loop, and then start them in each iteration, if they’re not running. Same for t2.

I’m not sure if threads are “reusable” like this, but there’s one way to find out… Try it and see what happens! (An exercise for the reader).

You could also read the threading module documentation carefully - but who has time for that?

For a more simple demo of why t1 is a different thread each iteration of the whole loop, try this:

def check(x):
    x += 1
    print(x)

while True:
    x = 1
    check(x)

this prints 2 forever

Vs.

def check(x):
    x += 1
    print(x)

x = 1

while True:
    check(x)

This… Huh? Also prints 2… Forever. Weird right?

This happens because the int is copied into the check function, instead of being modified in place.

What happens if you use an object that is passed by reference into the function, for example a list?

def check(xs):
    xs[0] += 1
    print(xs[0])

xs = [1]

while True:
    check(xs)

Sorry if I’m treading ground you’re already familiar with, I had fun explaining things

2 Likes

maybe a future (of concurrent) is more appropriate: threads are reused and and a ‘done callback’ may be added :woozy_face:

1 Like

Thanks for all the comments. It seems that below structure is doing what i need.

import time
import threading


def f1():
    
    while True:

        print('Hello ... 11');
        time.sleep(2);
        print('Hello ... 12'); 
        time.sleep(2);
        print('Hello .... 13'); 
        time.sleep(2);
        print('Hello .... 14'); 
        time.sleep(1);
        print('f1 finish')
    

def f2():
    
    while True:
        
        print('........World!..21'); 
        time.sleep(1);
        print('....... World!22'); 
        time.sleep(1);
        print('......World!..23'); 
        time.sleep(1);
        print('....... World!24');
        time.sleep(1);
        print('....... World!25');
        time.sleep(1);
        print('....... World!26');
        print('f2 finish')

def f3():
    print('........f3'); 


### execute functions on separate threads

threading.Thread(target=f1).start()
threading.Thread(target=f2).start()

#####  continous loop 

while True:

    threading.Thread(print('main loop new thread')).start() # one shot threaded
    threading.Thread(print('main loop')) # one shot
    threading.Thread(target=f3).start()
    print('unthreaded')  # unthreaded
    time.sleep(0.2)

For future reference.

What I do is start a pool of threads that all read from a queue.Queue() to get an item of work to do.

From the controlling code I put items into the queue each time I have something that needs doing in the background.

You can use a second queue for the background workers to post their results into that the controlling thread can read.

1 Like

These will perform their prints, then call threading.Thread(None) which doesn’t achieve much (it’ll create a thread with its group set to None, which is the default, and it has no target and therefore will do nothing).

I’m not sure what you expect the difference between these calls to be. One of the threads gets started, the other doesn’t. In theory, if they actually had targets, this would mean that the second one doesn’t actually execute, but the first one does; but I don’t understand your descriptions “one shot threaded” and “one shot”.

I’m not sure what is what yet, so i’ve been testing the difference between all functions of threading library. so: .start() and without .start(), is the same thing if you don’t want to create an object of threading and reuse it. those two lines are executing in a separate thread, not the main program thread.

My target was to execute separate functions continuously, in a separate thread. This is achieved by having a while True inside the function and thread it start only once.

Starting one thread/function to be called is very inefficient, is that what you are doing?
thread startup overhead is large.

Using a thread pool and having the threads pull work from a queue is a better design.
The threads are only started once.

the continuity of threads is just a preemptive fake and the switchinterval determines the frequency of preemption
so if the functions haven’t got anything to do with each other threading will be counterproductive :star_struck:
if you have lots of cores try processes: almost same interface and switching is done by the OS

And VERY different sharing semantics.

In other words in python if the threads are compute bound python code threads are not helpful. But if you are doing I/O bound functions it will help. If the compute bound are doing the work by calling i to C extensions like numpy then you can see a speed up.

It’s not NEARLY that simple, and threads absolutely can preempt each other. The switchinterval isn’t the only way that threads context-switch.

Hello everyone and many thanks for your contribution.

Let’s take a step backwards, so everyone understands the objective and then i’m willing to test any suggestion in real applications.

I have a modbus client code which read modbus values from field devices and writing modbus values to several devices. Modbus protocol on TCP originated from Serial protocol, so is based on request and answer from the requested device. For efficiency, parallel requests must be executed. The pulse feedback we need will be a modbus write command with value 0-1-0 for 3 seconds to simulate acceptance pulse. so a time.sleep(3) is needed.

At the same time, all other modbus reads and writes needs to executing in parallel. During this time.sleep(3) , everything else, all read/writes on modbus etc are stack. This is exactly what i’m trying to do : achieve a true multitasking behavior between the functions, just like a PLC that is able to execute all programs in a separate task , on a specific interval or trigger etc…

that’s why starting a new tread with target the function, it seems that is doing the job, but i’m open to try something else. I’m sure now is more clear. Thanks for any comments.