Reputation: 487
I'm working on an interface that communicates using sockets. I need to use Threads in order to receive and send data at the same time.
The below code is a cut down version of the module that continuously sends data.
My issue is that during development, I need to start and stop this frequently, but it seems threads makes it impossible to stop the application like you can a normal script. The process needs to be killed in process manager in order to restart it.
I have a feeling I am misunderstanding something.
I've looked around but I'm seeing loads of inconsistent answers which I've tried to implement to no avail. I've tried handling the KeyboardInterrupt exception and that doesn't work.
Any ideas?
import socket
import sys
import threading
running = True
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server_address = ('localhost', 50000)
print ('connecting to %s port %s' % server_address)
sock.connect(server_address)
class TelemetryPacket:
state = "N/A"
def __init__(self, stringvalue):
self.state = stringvalue
def serialise(self):
return "%s" % (self.state)
def send(running):
try:
while (running):
telemetry = TelemetryPacket(0).serialise()
sock.sendto(telemetry.encode('utf-8'), server_address)
except KeyboardInterrupt:
print ("threads successfully closed")
running = False
def recv(running):
try:
while (running):
data = sock.recv(1)
if (data):
print("data received: %s" % data)
except KeyboardInterrupt:
print ("threads successfully closed")
running = False
a = threading.Thread(target=send, args=(running,))
a.start()
b = threading.Thread(target=recv, args=(running,))
b.start()
Upvotes: 2
Views: 4581
Reputation: 25799
You should be capturing your your keyboard interrupt in the main thread and then sending a signal to your child threads to exit and join back to the main thread. You should also use something more thread safe for signaling like the basic threading.Event()
, so:
import threading
import time
exit_signal = threading.Event() # your global exit signal
def send():
while not exit_signal.is_set():
pass # whatever you need
def recv():
while not exit_signal.is_set():
pass # whatever you need
# create the threads
a = threading.Thread(target=send)
b = threading.Thread(target=recv)
# start the threads
a.start()
b.start()
# create a main thread loop
try:
while not exit_signal.is_set(): # enable children threads to exit the main thread, too
time.sleep(0.1) # let it breathe a little
except KeyboardInterrupt: # on keyboard interrupt...
exit_signal.set() # send signal to all listening threads
# join back the threads
a.join()
b.join()
# and you're done...
Upvotes: 3
Reputation: 476
The problem is that on keyboard exception only the main thread is killed. So you need to notify the threads that the main thread has been killed.
Instead of passing the running as an argument, use it to signal the threads.Here is an example code. Only difference is that function doesn't take running as an argument but it uses it to keep track. So when the main thread dies, we set it to false(at the end of the code).
import socket
import sys
import threading
running = True
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server_address = ('localhost', 50000)
print ('connecting to %s port %s' % server_address)
sock.bind(server_address)
class TelemetryPacket:
state = "N/A"
def __init__(self, stringvalue):
self.state = stringvalue
def serialise(self):
return "%s" % (self.state)
def send():
try:
while (running):
telemetry = TelemetryPacket(0).serialise()
sock.sendto(telemetry.encode('utf-8'), server_address)
except KeyboardInterrupt:
print ("threads successfully closed")
def recv():
try:
while (running):
data = sock.recv(1)
if (data):
print("data received: %s" % data)
except KeyboardInterrupt:
print ("threads successfully closed")
try:
a = threading.Thread(target=send)
a.start()
b = threading.Thread(target=recv)
b.start()
while True:
pass
except KeyboardInterrupt:
print ("threads successfully closed")
running = False # Kill the threads
Upvotes: 2