Как правильно завершить многопоточный скрипт на Python
Пишу чат, есть серверная и клиентская часть, работает по TCP. Реализован с помощью многопоточности.
Возникла проблема, как правильно завершать работу таких скриптов? В данном случае, в __init__ в потоке MainThread, вызывает функцию send_message, в потоке Thread-1.
__init__ в цикле проверяет, если kill_treads = False то цикл работает. По /stop, я переключаю kill_treads в True, что вызывает остановку MainThread, и Thread-1 становится MainThread. Я проверяю, если функция send_message находится в потоке MainThread, то кидаю выход.
Но, после ввода /stop, выход не происходит, приходится вводить ещё что-то что бы выключилось.
Как это можно исправить и правильно завершить многопоточный скрипт?
import socket
import threading
import sys
class Client:
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
def send_message(self):
while True:
if threading.currentThread().getName() == 'MainThread':
# если текущий поток главный, то выходим
print('Exit')
self.sock.close() # закрываем соединение
sys.exit(1) # выходим из скрипта
try:
# print('Send message: ', end='')
message = input()
if message == '/stop': # для выхода из программы
self.kill_treads = True # выключает главный цикл
else:
# печатаем сообщение
self.sock.send(bytes(message, 'utf-8'))
except EOFError:
print('[Something gona wrong. Connection lost]')
self.kiil_treads = True
def __init__(self):
self.kill_treads = False
# запускаем сервер, заставляя слушать локалхост
self.sock.connect(('localhost', 4242))
print('Соединение активированно\n')
thread = threading.Thread(target=self.send_message)
thread.daemon = True
thread.start()
while not self.kill_treads:
data = self.sock.recv(1024)
if not data:
break
print(str(data, 'utf-8'))
print(data)
if __name__ == '__main__':
client = Client()
client.send_message()
Ответы (2 шт):
Конкретно в этом случае проще всего закрыть сокет:
if message == '/stop': # для выхода из программы
self.kill_treads = True # выключает главный цикл
self.sock.close() # закрывает сокет. В читающем потоке будет брошено исключение
Брошенное исключение, нужно поймать и обработать, чтобы програма не завершалась аварийно:
try:
data = self.sock.recv(1024)
except Exception as e:
break
У вас зависает вызов self.sock.recv, который находится в главном потоке и препятствует завершению программы. Вынесите прослушку ответов в отдельный поток. Притом, поток на прослушку надо вызывать как демон, а вот на ввод – нет, т.к основной поток должен ждать его завершения.
import socket
import threading
import sys
class Client:
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
def send_message(self):
while True:
if threading.currentThread().getName() == 'MainThread':
# если текущий поток главный, то выходим
print('Exit')
self.sock.close() # закрываем соединение
sys.exit(1) # выходим из скрипта
try:
# print('Send message: ', end='')
message = input('Type:')
print(message)
if message == '/stop': # для выхода из программы
self.kill_treads = True # выключает главный цикл
break
else:
# печатаем сообщение
self.sock.send(bytes(message, 'utf-8'))
except EOFError:
print('[Something gona wrong. Connection lost]')
self.kiil_treads = True
print('Finished send_message')
def listen(self):
while not self.kill_treads:
data = self.sock.recv(1024)
if not data:
break
print('Got data:')
print(str(data, 'utf-8'))
print('Finished listen')
def __init__(self):
self.kill_treads = False
self.sock.connect(('localhost', 8000))
print('Соединение активированно\n')
thread = threading.Thread(target=self.send_message)
thread.daemon = False
thread.start()
thread = threading.Thread(target=self.listen)
thread.daemon = True
thread.start()
if __name__ == '__main__':
client = Client()