0

I am using win10 and python 3.7.3 32-bit

I am trying to achieve the following: every second a data read-out is performed an printed, while a control loop waits for user input to control a device. see my code:

import device
import threading
from threading import Lock

def print_data(t_start):
    while True:
        data=getData() # generic example
        print(f'data: {data} at time {time.time()-t_start}')

def control_func():
    while True:
        in_ = input(' Press ENTER to switch ON, Press x to quit')
        if in_ == '':
            device.on()
            print('device ON')
        elif in_ == 'x' or in_ == 'X':
            device.off()
            sys.exit()
        else: continue

        in_ = input(' Press ENTER to switch OFF, Press x to quit')
        if in_ == '':
            device.off()
            print('device OFF')
        elif in_ == 'x' or in_ == 'X':
            device.off()
            sys.exit()
        else: continue

t_start = time.time()
device=device()

trd1 = threading.Thread(target=control_func())
trd2 = threading.Thread(target=print_data(t_start))


trd1.start() # starting the thread 1 
trd2.start() # starting the thread 2

trd1.join()
trd2.join()

This only gives me the input statements from control_func() or the prints from print_data()

Same wih using mutliprocessing. I didn't manage to make the two functions run simultanously. replacing print() with

s_print_lock = Lock()
# Define a function to call print with the Lock

def s_print(*a, **b):
    """Thread safe print function"""
    with s_print_lock:
        print(*a, **b)

also didn't do the trick. Since I am noob, please help. Or should I do a different approach all together?

Stephan
  • 1
  • 1
  • 1
    it might has to do with the fact that the print of the subprocesses are buffered. try to do a `sys.stdout.flush()` after the print. – user_na Jul 11 '22 at 13:01
  • 1
    @user_na: Easier: Add `flush=True` as an argument to each `print` call; it's been supported since 3.3 (give or take). – ShadowRanger Jul 11 '22 at 13:03

1 Answers1

1

You called the functions in the process of creating the Thread, you didn't pass the functions to the Thread for it to execute, so no actual work occurred in the threads. Change the Thread creation to:

trd1 = threading.Thread(target=control_func)  # Remove call parens
trd2 = threading.Thread(target=print_data, args=(t_start,))  # Remove call parens and pass args tuple separately

so the functions themselves are passed, and the Thread actually runs them in the separate logical thread of execution. As written, you ran control_func to completion, then ran print_data to completion, then launched two Threads with target=None (the returned value from both functions) which do nothing, then joined the Threads doing nothing.

Additional notes:

  1. If you're using multiprocessing, make sure to use multiprocessing.Lock, not threading.Lock (the latter is only guaranteed to work within a single process)
  2. While the threaded cases likely doesn't require a lock (at least on CPython where the GIL protects against issues) if you're only printing a single atomic thing at a time, for multiprocessing you should definitely use the lock and add flush=True to all prints; without the flush=True, the actual output might be delayed indefinitely.
  3. You need to provide some means to communicate that the loop is done to print_data; as written, control_func will sys.exit(), but that only exits that thread, not the program. Unless getData will somehow throw an exception as a result of control_func exiting, print_data will never exit, and therefore neither will the main thread.

Solutions for #3 include:

  1. Using a threading.Event(); make a global should_stop = threading.Event(), change the print_data loop to while not should_stop.is_set():, and after trd1.join() returns, have the main thread call should_stop.set()
  2. Make trd2 a daemon thread, and don't bother to join it (assumes it can die immediately when trd1 and the main thread have ended; might not hold for your scenario); it will die forcefully when all non-daemon threads have exited.
ShadowRanger
  • 143,180
  • 12
  • 188
  • 271