Buongiorno a tutti.
Finalmente ho risolto il problema, lascio qui la soluzione sperando possa essere utile a qualcun altro della Community.

La soluzione l'ho trovata con Ray
https://ray.io/, un pacchetto che offre, con pochi comandi, la gestione dei subprocessi in modo eccellente.
Oltre ad importarlo con il classico import ray, bisogna inserire all'inizio del codice il comando ray.init() e inserire il decoratore @ray.remote prima della classe o della funzione che si vuole "mandare" nel sotto processo.
La cosa più complessa è stata, per me, gestire il flusso dei dati di ritorno perchè nelle istruzioni non era ben chiaro il meccanismo, mi spiego meglio:
se ho due sotto processi di durata differente la funzione ray.get(miaFunzione) preleva i dati aspettando che tutti i sottoprocessi siano completati, per questo va utilizzata la funzione ray.wait(miaFunzione) che restituisce due liste (qui sono impazzito fin quando non ho trovato questo articolo
https://medium.com/distributed-computing-with-ray/ray-tips-and-tricks-part-i-ray-wait-9ed7a0b9836d), la prima contiene lo/gli oggetto/i di tipo Actors o Functions risolti, la seconda quelli non ancora pronti. Si deve eseguire la ray.get() solo sulla prima lista. Un altro consiglio che mi sento di dare è: se avete necessità, come ne mio caso, di eseguire questi processi all'infinito, bisogna rimuovere dalla lista delle funzioni quelle risolte perchè non vengono rimosse in automatico (questo mi è sembrato strano, ma non sono riuscito a fare di meglio che toglierle manualmente).
Inserisco qui sotto il mio prog. di prova, ci sono delle classi modificate da me quindi non vi funzionerà però spero che il listato possa aiutare nella comprensione. Ah dimenticavo, questi sono i tempi di esecuzione delle letture:
Ready length, values: 1 ('/dev/ttyUSB1', [[0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]])
Not Ready length: 1
duration com=
0.017377614974975586Ready length, values: 1 ('/dev/ttyUSB0', [[218.3000030517578], [0.06800000369548798], [6.300000190734863], [6.4808149337768555], [0.0], [0.9767923951148987], [50.0], [6.712806701660156], [1.7009999752044678]])
Not Ready length: 1
duration com=
0.0005311965942382812comunicazione0 = {"port":"/dev/ttyUSB1","baudrate":115200,"parity":"N","bytesize":8,"stopbits":1,"method":"rtu","timeout":0.005}
comunicazione1 = {"port":"/dev/ttyUSB0","baudrate":9600,"parity":"N","bytesize":8,"stopbits":1,"method":"rtu","timeout":0.5}
data0 = [{"unit":1,"funz":0x01,"address":0x0000,"count":0x08,"tipo":"OU"},
{"unit":2,"funz":0x02,"address":0x0000,"count":0x10,"tipo":"DI"},
# {"unit":3,"funz":0x03,"address":0x0BB8,"count":0x04,"tipo":"AO","timeout":0.05},
{"unit":4,"funz":0x02,"address":0x0000,"count":0x10,"tipo":"DI"},
{"unit":5,"funz":0x02,"address":0x0000,"count":0x10,"tipo":"DI"},
{"unit":6,"funz":0x02,"address":0x0000,"count":0x10,"tipo":"OU"}]
data1 = [{"unit":1,"funz":0x04,"count":0x02,"address": 0,"typeVal":"float"},
{"unit":1,"funz":0x04,"count":0x02,"address": 6,"typeVal":"float"},
{"unit":1,"funz":0x04,"count":0x02,"address": 12,"typeVal":"float"},
{"unit":1,"funz":0x04,"count":0x02,"address": 18,"typeVal":"float"},
{"unit":1,"funz":0x04,"count":0x02,"address": 24,"typeVal":"float"},
{"unit":1,"funz":0x04,"count":0x02,"address": 30,"typeVal":"float"},
{"unit":1,"funz":0x04,"count":0x02,"address": 70,"typeVal":"float"},
{"unit":1,"funz":0x04,"count":0x02,"address": 86,"typeVal":"float"},
{"unit":1,"funz":0x04,"count":0x02,"address":342,"typeVal":"float"}]
import MJTool
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *
from PyQt5.QtCore import *
import ray
import time
class MainWindow(QWidget):
def __init__(self):
super().__init__()
self.setFixedSize(400,400)
self.mainLayout = QVBoxLayout(self)
self.layout0 = QHBoxLayout()
self.layout1 = QVBoxLayout()
self.lbl1 = QLabel()
self.lbl2 = QLabel()
self.btnCnt = QPushButton('Cnt1')
self.btnCnt.setCheckable(True)
self.layout0.addWidget(self.btnCnt)
self.layout1.addWidget(self.lbl1)
self.layout1.addWidget(self.lbl2)
self.mainLayout.addLayout(self.layout0)
self.mainLayout.addLayout(self.layout1)
def setText(self,val1=None,val2=None):
if val1 != None:
self.lbl1.setText(str(val1))
if val2 !=None:
self.lbl2.setText(str(val2))
def closeEvent(self, event):
self.close()
print("main process finished")
ray.init()
@ray.remote
class Com0():
def __init__(self,com,dati):
self.com = com
self.dati = dati
self.client = MJTool.MJModbus_tk() #Pacchetto MOdbus_tk modificato (esegue anche la funzione 17)
self.val = []
def comunicaCOM(self):
self.val.clear()
for dato in self.dati:
self.client.comunica(dato)
self.val.append(self.client.valore)
return self.client.client._serial.port, self.val #aggiungo il valore della porta seriale per riconoscere la funzione di ritorno
def com_avvio(self):
self.client.connetti(self.com)
f=[]
if __name__ == "__main__":
import sys
app = QApplication(sys.argv)
main = MainWindow()
com0 = Com0.remote(comunicazione0,data0)
com0.com_avvio.remote()
com1 = Com0.remote(comunicazione1,data1)
com1.com_avvio.remote()
timer = QTimer(main)
timer.start()
def setTimer():
global f
if main.btnCnt.isChecked():
start = time.time()
if len(f) == 0: #inizializzo la lista funzioni
f.append(com1.comunicaCOM.remote())
f.append(com0.comunicaCOM.remote())
ready, not_ready = ray.wait(f)
valore = ray.get(ready)[0]
print('Ready length, valori in Ready: ', len(ready), valore)
print('Not Ready length:', len(not_ready))
f.pop(f.index(ready[0]))
if valore[0] == comunicazione1["port"]:
main.setText(val2=valore)
f.append(com1.comunicaCOM.remote())
elif valore[0] == comunicazione0["port"]:
main.setText(valore)
f.append(com0.comunicaCOM.remote())
print("duration com=", time.time() - start)
timer.timeout.connect(setTimer)
main.show()
sys.exit(app.exec_())