How to close sockets of a class from a non class - python

my script works like this:
# other part of code
class request(threading.Thread):
def __init__(self):
threading.Thread.__init__(self)
def run(self):
while True:
try:
socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
socket.connect((host), (port))
socket.send(str.encode("test"))
except:
socket.close()
def loop():
for x in range(5):
request(x).start()
# other
# part
# of code
def startall():
# some other code
choice = input("command: ")
if choice == "request":
loop()
elif choice == "stop":
# ?
# some other code
startall()
Is there a way to stop sending request if the input is "stop"? Note that this is just a sample, my script doesn't work like this. I put this code just to let you understand what is my problem

If you want to stop all requests at once you can modify your class as folows:
class request(threading.Thread):
REQUESTS_ALLOWED = True
def run(self):
while request.REQUESTS_ALLOWED:
socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
try:
socket.connect((host), (port))
socket.send(str.encode("test"))
except:
pass # Do what you need
finally:
socket.close()
Notice the alternation of closing socket. In your code the socket was closed when garbage collector destroyed your variable socket. With my alternation it's guaranteed that socket is closed with every iteration.
The start and stop events now can change the state of all request objects.
if choice == "request":
request.REQUESTS_ALLOWED = True
loop()
elif choice == "stop":
request.REQUESTS_ALLOWED = False
After setting REQUESTS_ALLOWED to False you should join() all running threads. It's just recommendation (and you don't have to do it) because normally when function returns it indicates that something is done. So after return from function startall() with choice = "stop" I would expect that all started threads are stopped.
Full code example:
import threading
import time
class Request(threading.Thread):
REQUESTS_ALLOWED = True
active_threads = set()
def __init__(self):
threading.Thread.__init__(self)
def start(self):
Request.active_threads.add(self) # Add thread to set for later use
super().start()
def run(self):
while Request.REQUESTS_ALLOWED:
print("Thread {} is alive.".format(self.name))
time.sleep(1)
print("Thread {} is done.".format(self.name))
def loop():
for x in range(5):
Request().start()
def startall(choice):
if choice == "request":
Request.REQUESTS_ALLOWED = True
loop()
elif choice == "stop":
Request.REQUESTS_ALLOWED = False
# Iterate through active threads and wait for them
for thread in Request.active_threads:
thread.join()
Request.active_threads.clear()
startall("request")
time.sleep(3)
startall("stop")
The code was tested in Python 3.6.1

Related

Python: How to terminate multithreaded python program?

I want to create one program in which two lists of hosts are available. I want to read data from each host. It will take around 5-10 seconds so I want to read each host data with different thread.
I created below code and it is working as per my expectations but only problem is when I'm pressing Ctrl+c, program didn't terminate.
My code:
import threading
import time,os,sys
import signal
is_running = True
def signal_handler(signal, frame):
print "cleaning up...please wait..."
v1.stop()
v2.stop()
global is_running
is_running = False
class Thread2(threading.Thread):
def __init__(self, function,args):
self.running = False
self.function = function
self.args = args
super(Thread2, self).__init__()
def start(self):
self.running = True
super(Thread2, self).start()
def run(self):
while is_running:
self.function(self.args)
time.sleep(time_interval)
def stop(self):
self.running = False
def b_iterate(hostnames):
for host_name in hostnames:
v = Thread2(function = read_cet_data,args = host_name)
v.start()
def read_b_data(host):
#
#reading some data from current host (5-10 seconds processing)
#
#here, this thread is not neccessary, want to stop or kill or terminate it
if threading.current_thread().isAlive():
threading.current_thread().stop()
def a_iterate(entp_hostnames):
for host_name in entp_hostnames:
v = Thread2(function = read_entp_data,args = host_name)
v.start()
def read_a_data(host):
#
#reading some data from current host (5-10 seconds processing)
#
#here, this thread is not neccessary, want to stop or kill or terminate it
if threading.current_thread().isAlive():
threading.current_thread().stop()
if __name__ == "__main__":
signal.signal(signal.SIGINT, signal_handler)
#a_hostnmaes & b_hostnmaes are the lists of hostnames
v1 = Thread2(function = a_iterate,args = a_hostnames)
v2 = Thread2(function = b_iterate,args = b_hostnames)
v1.start()
v2.start()
while is_running:
pass
How I can make this program terminate after pressing Ctrl+c. Am I missing something?
If you just want control C to finish everything, there is no need to use a stop function in threads. You can just daemonise them:
v1 = Thread2(function = a_iterate,args = a_hostnames)
v2 = Thread2(function = b_iterate,args = b_hostnames)
v1.daemon = True
v2.daemon = True
v1.start()
v2.start()
As soon as your main program dies, these threads die as well. You need to add .daemon = True to all other locations in the code where a thread is created.
Hannu
You can either
catch KeyboardInterrupt in main thread
set a flag so another threads can detect it and exit
or
catch KeyboardInterrupt
call os._exit()

Why can I launch a thread (with the python threading module) only once?

I have simple script for watchdog on network device. Script monitors response from PING command. If there is no answer then second thread executes and first thread is stopped. If second thread is finished then first thread is resumed (checking ping). If there is no answer then following message appears:
RuntimeError: threads can only be started once
Here is my code:
#!/usr/bin/python
import os
import time
import sqlite3
from ablib import Pin
import threading
led=Pin('W9','OUTPUT')
class threadout1(threading.Thread):
def run(self):
while True:
conn = sqlite3.connect('database/database.db')
cur = conn.cursor()
cur.execute("SELECT * FROM watchdog")
rows_output = cur.fetchall()
time.sleep(1)
if rows_output[0][1] == "ping":
response = os.system("ping -c 1 " + rows_output[0][2])
if response != 0:
print "bad"
rest.start()
rest.join()
class restart(threading.Thread):
def run(self):
led.on()
time.sleep(15)
led.off()
thr = threadout1()
rest = restart()
thr.start()
You can either create the restart thread every time you need it
if response != 0:
print "bad"
restart_thread = restart()
restart_thread.start()
restart_thread.join()
or use Events
class restart_thread(threading.Thread):
def __init__(self, evt):
self.evt = evt
def run(self):
self.evt.wait()
# do stuff
self.evt.clear()
class threadout(threading.Thread):
def __init__(self, evt):
self.evt = evt
def run(self):
if #other thread needs to run once
self.evt.set()
evt = threading.Event()
restart_thread = restart(evt)
restart_thread.start()
pinging_thread = threadout(evt)
pinging_thread.start()
To make the pinging_thread wait for the restart_thread to finish, you could use another Event.

running threads inside imported class in python

I am experimenting with a GSM900 modem a raspberry pi and python and I am trying to make a simple libary for use with a GSM900 rpi board that uses a ttl serial connection.
however I am having issues getting threading to work as expected, as soon as I import the library(Serialworker.py) and use the start() function from the serialworker class in my main python application(sniffer.py) the code blocks at the start() function and doesnt continue the main.py code
I have the Following main python file:
main.py
#!/usr/bin/python3
from time import sleep
try:
import RPi.GPIO as GPIO
GPIO.setwarnings(False)
GPIO.setmode(GPIO.BCM)
GPIO.setup(17, GPIO.OUT, initial=0)
except ImportError:
print("Could not control GPIO\n"
"Are you root?")
exit()
from serialworkerpi import serialworker
def powercycle():
try:
GPIO.output(17, 0)
sleep(0.2)
GPIO.output(17, 1)
except:
print("Setup Failed, Are you root?")
def online():
if not serworker.checkgsm900online():
print("GSM900 not online, cycling power")
powercycle()
while not serworker.checkgsm900online():
print("Waiting for GSM900")
else:
print("GSM900 connection found")
print("Completed boot cycle")
if __name__ == '__main__':
"""main loop"""
serworker = serialworker()
online()
serworker.start()
try:
while serworker.check():
print("loop")
sleep(0.5)
except KeyboardInterrupt:
serworker.stop()
print("Shutting down threads")
and this library class which holds the start() function
Serialworker.py:
class serialworker():
"""This Class instantiates a serial thread and has functions for facilitating communication
to the serial interface as well as some GSM900 specific functions"""
def __init__(self, port='/dev/ttyAMA0', baud=115200, timeout=5, pollspeed=0.1):
"""Initialises the variables used in this class, baud=, timeout= and pollspeed= can be adjusted
But have initial values of 115200, 5 and 0.1 respectively, in the future i could add stopbits and parity here"""
self.port = port
self.baud = baud
self.timeout = timeout
self.pollspeed = pollspeed
self.run = False
self.ser = serial.Serial
"""Command variable description:
self.command = Command to write in next while cycle
self.commandwait = Bool used to determine if write buffer contains a command for writing in the next while cycle
self.commandstat = Variable used to hold the return status of the command that was submitted
self.commandret = Bool used to determine if a command was written to the buffer, as to allow the output to be read in the next while cycle
self.respwait = Bool used to determine if commandstat is ready to be read
"""
self.command = ""
self.commandwait = False
self.commandstat = ""
self.commandret = False
self.respwait = False
def start(self):
"""Starts a thread of the _readwriteloop() funtion"""
#TODO add thread checking, only 1 thread per serial interface should be allowed
self.run = True
t1 = threading.Thread(target=self.readwriteloop())
t1.start()
print("started")
def checkgsm900online(self):
"""Checks if the GSM900 board is online"""
gsmser = serial.Serial(
port=self.port,
baudrate=self.baud,
parity=serial.PARITY_NONE,
stopbits=serial.STOPBITS_ONE,
bytesize=serial.EIGHTBITS,
timeout=self.timeout,)
gsmcheck = ("AT"+"\r\n").encode()
gsmser.write(gsmcheck)
sleep(0.03)
gsmbuffer = gsmser.inWaiting()
if gsmbuffer == 0:
gsmser.flush()
gsmser.close()
return False
else:
gsmser.flush()
gsmser.close()
return True
def check(self):
"""Checks if a thread currently exists"""
return self.run
def stop(self):
"""stops running thread"""
self.run = False
def inputcommand(self, string, flag):
"""Allows for a single command to be inputted, sets the commandwait to true to tell the loop to check its queue.
Optionally you can set flag=False to not wait for a return stat on this command"""
self.command = (string+"\r\n").encode()
self.commandwait = True
print("waiting for resp")
if flag:
while not self.respwait:
sleep(0.1)
self.respwait = False
return self.commandstat
def readwriteloop(self):
"""Main function of the class that reads serial data from a buffer
And checks for new commands inputted by the inputcommand() function
which output status it will write to the commandstat buffer"""
#TODO write function for retrieving input command return data not just the status
ser = self.ser(
port=self.port,
baudrate=self.baud,
parity=serial.PARITY_NONE,
stopbits=serial.STOPBITS_ONE,
bytesize=serial.EIGHTBITS,
timeout=self.timeout,)
while self.run:
ser.inWaiting()
buffer = ser.inWaiting()
if buffer != 0:
decodeline = ser.readline().decode('utf-8', "ignore").rstrip('\n')
if len(decodeline) > 2:
if self.commandret:
if 'ERROR' in decodeline:
self.commandstat = decodeline
self.commandret = False
self.respwait = True
elif 'OK' in decodeline:
self.commandstat = decodeline
self.commandret = False
self.respwait = True
elif 'NO CARRIER' in decodeline:
self.commandstat = decodeline
self.commandret = False
self.respwait = True
if self.commandwait:
ser.write(self.command)
#print(self.command)
self.commandwait = False
self.commandret = True
sleep(self.pollspeed)
The weird thing is that when i run the class from within Serialworker.py itself
by for instance appending this code to the end of the file:
if __name__ == '__main__':
x = serialworker()
x.start()
i = 0
while i != 1000:
i += 1
x.check()
sleep(0.01)
pin = "0000"
pinconvert = "AT+CPIN=\"%s\"" % pin
succeed = x.inputcommand(pinconvert, True)
print(succeed)
while i != 10000:
i += 1
x.check()
sleep(0.01)
x.stop()
isonline = x.checkgsm900online()
print(isonline)
it works as expected and the start() function does not block and runs for as long as the while loop continues to run
The problem is that you're calling the function that's supposed to be executed by a thread:
Thread(target=self.readwriteloop())
should be
Thread(target=self.readwriteloop)

Run function in background and continue with program

I am trying to run a function in the background, whilst continuing with said code in python.
The function I want to run in the background is from socket. Looking for specific data to cut the program off.
Here is the function:
def receive():
host = ""
port = 13000
buf = 1024
addr = (host,port)
Sock = socket(AF_INET, SOCK_DGRAM)
Sock.bind(addr)
(data, addr) = Sock.recvfrom(buf)
return data
Here is the code I want to run:
while True:
r = receive()
if r == "stop":
break
#Cannot get past here, because of the function running.
#Should loop over and over, until stop data is received
print "Running program"
I have tried threading, with r = threading.Thread(target=receive()) with no joy.
Rookie error:
r = threading.Thread(target=receive())
I did not take the brackets off the receive():
r = threading.Thread(target=receive)
You can't return to the invoking thread from an invoked thread's target function. Instead, you need some inter-thread communication system. Below, is an example using Python's Queue to pass received datagrams between the two threads. I've used a threading.Event to signal when the receiver thread should stop.
#!/usr/bin/env python
import socket
import threading
from queue import Empty, Queue
class DatagramReceiver(threading.Thread):
def __init__(self, stop, queue):
super().__init__()
self._stop = stop
self._queue = queue
def run(self):
with socket.socket(AF_INET, SOCK_DGRAM) as sock:
sock.bind(('', 13000))
while not self._stop.is_set():
data = sock.recvfrom(1024)[0]
if data == 'stop':
self._stop.set()
break
self._queue.put(data)
def main():
stop = threading.Event()
queue = Queue()
reader = DatagramReceiver(stop, queue)
reader.deamon = True
reader.start()
while not stop.is_set():
user_input = input('Press RETURN to print datagrams, or q quit')
if user_input == 'q':
break
while True:
try:
datagram = queue.get_nowait()
except Empty:
break
print(datagram)
stop.set()
reader.join()

Multithreading (?): Manual interference in a loop

I've been looking into a way to directly change variables in a running module.
What I want to achieve is that a load test is being run and that I can manually adjust the call pace or whatsoever.
Below some code that I just created (not-tested e.d.), just to give you an idea.
class A():
def __init__(self):
self.value = 1
def runForever(self):
while(1):
print self.value
def setValue(self, value):
self.value = value
if __name__ == '__main__':
#Some code to create the A object and directly apply the value from an human's input
a = A()
#Some parallelism or something has to be applied.
a.runForever()
a.setValue(raw_input("New value: "))
Edit #1: Yes, I know that now I will never hit the a.setValue() :-)
Here is a multi-threaded example. This code will work with the python interpreter but not with the Python Shell of IDLE, because the raw_input function is not handled the same way.
from threading import Thread
from time import sleep
class A(Thread):
def __init__(self):
Thread.__init__(self)
self.value = 1
self.stop_flag = False
def run(self):
while not self.stop_flag:
sleep(1)
print(self.value)
def set_value(self, value):
self.value = value
def stop(self):
self.stop_flag = True
if __name__ == '__main__':
a = A()
a.start()
try:
while 1:
r = raw_input()
a.set_value(int(r))
except:
a.stop()
The pseudo code you wrote is quite similar to the way Threading / Multiprocessing works in python. You will want to start a (for example) thread that "runs forever" and then instead of modifying the internal rate value directly, you will probably just send a message through a Queue that gives the new value.
Check out this question.
Here is a demonstration of doing what you asked about. I prefer to use Queues to directly making calls on threads / processes.
import Queue # !!warning. if you use multiprocessing, use multiprocessing.Queue
import threading
import time
def main():
q = Queue.Queue()
tester = Tester(q)
tester.start()
while True:
user_input = raw_input("New period in seconds or (q)uit: ")
if user_input.lower() == 'q':
break
try:
new_speed = float(user_input)
except ValueError:
new_speed = None # ignore junk
if new_speed is not None:
q.put(new_speed)
q.put(Tester.STOP_TOKEN)
class Tester(threading.Thread):
STOP_TOKEN = '<<stop>>'
def __init__(self, q):
threading.Thread.__init__(self)
self.q = q
self.speed = 1
def run(self):
while True:
# get from the queue
try:
item = self.q.get(block=False) # don't hang
except Queue.Empty:
item = None # do nothing
if item:
# stop when requested
if item == self.STOP_TOKEN:
break # stop this thread loop
# otherwise check for a new speed
try:
self.speed = float(item)
except ValueError:
pass # whatever you like with unknown input
# do your thing
self.main_code()
def main_code(self):
time.sleep(self.speed) # or whatever you want to do
if __name__ == '__main__':
main()

Categories

Resources