I'm conscious that there are several variants according to which version of python.
I'm running on a Pi3 with python 3.4.2 or 2.7.9
I'm running micropython version 1.11 on several esp32's
I'll constrain my example to python 3.4.2 for simplicity
Why do I wish to do this?
The Pi3 runs the out control loop: with some interrupt handling, but mainly tkinter control and monitoring pages.
It serves a ring of ESP32 microcontrollers running micopython, who communicate with each other via WIFi local network (one can act as an access point). One is nominated to receive and distribute messages via tcp sockets from the array of ESP32's which are responding to interrupts from sensors, sending PWM control to servos. This is the inner loop monitoring and control.
There are multiple threads on each controller, interruptible
I'm an aging SW engineer chap who has been familiar with inner and outer loop control, ISR and so forth.
So with onboard wifi and a plethora of interrupt pins + PWM what better way than use sockets.
The first thing that I learned was that socket do block by default, so no main programme can run.
The second thing that I discovered that threads become similarly blocked.
So I ended up with asyncio, with threads.
However:
I'm having difficulty (mainly because of the several differing implementations coupled with sparse descriptions of operation of functions) understanding the mechanisms and interaction of asyncio with threads.
This is a cut down version of the programme snippet. It would represent one small element of the application. It merely responds to clients.
The socket mechanism works and the connection behaves. The tasks run. However, the asyncio loop and run_until_complete are in the main programme and block all main body code.
Code: Select all
#asta.py
import asyncio, socket, time,threading
class mythread(threading.Thread):
def run(self):
while True:
print ("In Thread",self.name)
time.sleep(2)
th1=mythread()
th1.setName("T1")
th1.start()
th2=mythread()
th2.setName("T2")
time.sleep(1)
th2.start()
@asyncio.coroutine
def handle_client(client):
request = None
while request != 'quit':
request = (yield from loop.sock_recv(client, 255)).decode('utf8')
response = request + '\n'
yield from loop.sock_sendall(client, response.encode('utf8'))
client.close()
@asyncio.coroutine
def run_server():
while True:
client, _ = yield from loop.sock_accept(server)
loop.create_task(handle_client(client))
HOST='192.168.1.241'
PORT=8888
server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server.bind((HOST,PORT))
server.listen(8)
server.setblocking(False)
loop = asyncio.get_event_loop()
loop.run_until_complete(run_server())
while True:
print ("Running")
time.sleep(.05)
So the next stage was convert the "loop = asyncio.get_event_loop(), loop.run_until_complete(run_server())" to either a thread class, insatiate an object and start it, or as here, define it as a function, and start it as a thread
Code: Select all
#astb.py
import asyncio, socket, time,threading
class mythread(threading.Thread):
def run(self):
while True:
print ("In Thread",self.name)
time.sleep(2)
th1=mythread()
th1.setName("T1")
th1.start()
th2=mythread()
th2.setName("T2")
time.sleep(1)
th2.start()
def rserver():
loop=asyncio.get_event_loop()
loop.run_until_complee(run_serer())
@asyncio.coroutine
def handle_client(client):
request = None
while request != 'quit':
request = (yield from loop.sock_recv(client, 255)).decode('utf8')
response = request + '\n'
yield from loop.sock_sendall(client, response.encode('utf8'))
client.close()
@asyncio.coroutine
def run_server():
while True:
print("Start Server")
client, _ = yield from loop.sock_accept(server)
loop.create_task(handle_client(client))
HOST='192.168.1.241'
PORT=8888
server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server.bind((HOST,PORT))
server.listen(8)
server.setblocking(False)
#loop = asyncio.get_event_loop()
#loop.run_until_complete(run_server())
ts=threading.Thread(target=rserver)
ts.start()
while True:
print ("Running")
time.sleep(.05)
In this case I get:-
"^CException ignored in: <module 'threading' from '/usr/lib/python3.4/threading.py'>
Traceback (most recent call last):
File "/usr/lib/python3.4/threading.py", line 1294, in _shutdown
t.join()
File "/usr/lib/python3.4/threading.py", line 1060, in join
self._wait_for_tstate_lock()
File "/usr/lib/python3.4/threading.py", line 1076, in _wait_for_tstate_lock
elif lock.acquire(block, timeout):
KeyboardInterrupt
"
In the case of instantiating a class object, just silence.
How can I run a server as a non blocking task?
It seems there are hundreds of us wishing to do this, but I've not yet found a workable elegant and not overly complicated example for such a simple task.
I'd really welcome some help.
Thanks
Robin