Как работает асинхронность в пайтон?
asyncio - это, скорее, как обойтись без многопоточности.
Она хорошо работает для ситуаций, когда большая часть времени потока выполнения уходит на ожидание ввода-вывода - например, на ответ по сети. Если у тебя есть что-то, что занимает CPU, или любая другая операция, выполняемая вне рамок ввода-вывода через asyncio - это остановит выполнение асинхронной программы.
Если на пальцах, то асинхронная программа состоит из отдельных корутин. Разница с потоками в том, что потоки переключает ОС, а корутины сами отдают управление, когда уходят в ожидание события (грубо говоря, когда делают вызов с await). Это упрощает синхронизацию корутин, так как ты точно знаешь, когда твоя корутина отдаст управление - меньше шансов налететь на состояние гонки из-за внезапного переключения. Опять же, переключение корутин куда более детерминировано, чем переключение потоков, так что "плавающие" баги встречаются реже.
Тело асинхронной программы - это рабочий цикл (т.н. реактор, он же loop), который проверяет выполняемые операции ввода-вывода, и возобновляет выполнение корутины, когда её операция завершилась. Когда корутина завершится или запланирует ещё одну операцию, она вернёт управление в цикл. Вот почему "задумавшаяся" корутина остановит всю программу.
Минус в том, что в отличие от многопоточности, нельзя просто вжжжжжжух и сделать код асинхронным. Он должен писаться как асинхронный с самого начала, с прицелом на исполнение в реакторе.
threading не умеет закрывать потоки, по этому такое решение с этой библиотекой, при закрытии окна запускается функция closer(). Она закрывает pyqt окно а потом читает процессы и убивает процесс php сервера.
#!/usr/bin/python3
# -*- coding: utf-8 -*-
from random import *
import sys,os,threading
from PyQt5 import QtCore, QtGui, QtWidgets
from PyQt5.QtCore import *
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *
from PyQt5.QtWebKit import *
from PyQt5.QtWebKitWidgets import *
from PyQt5.QtWidgets import QApplication, QWidget, QMainWindow

port = str(randint(9000, 9999))
url = "http://localhost:"+port
comm = "php -S localhost:"+port+" -t app/"
icon = "app/icon.ico"
title = "Qt Web App"
dw = 1920
dh = 1080
ww = 800
wh = 600

def closer():
    app.exec_()
    os.system("ps -ef | grep \""+comm+"\" > .pid ")
    proc = open(".pid")
    proc = proc.readlines()
    for line in proc:
        line = line.split()
        pid = line[1]
        os.system("kill "+pid)
    os.remove('.pid')

def run_server():
    os.system(comm)

server = threading.Thread(target=run_server)
server.start()

app = QApplication(sys.argv)
web = QWebView()
web.load(QUrl(url))
web.setGeometry(int((dw-ww)/2), int((dh-wh)/2), ww, wh)
web.setWindowTitle(title)
web.setWindowIcon(QIcon(icon))
web.show()
sys.exit(closer())