-
Notifications
You must be signed in to change notification settings - Fork 3
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
RFC-1029 支持多线程运行模式 #85
Comments
Swoole v6 要来了!即将增加多线程支持 以后是不是可以写成类似python这样,无论是使用多进程、还是多线程,换个单词即可 import time
from concurrent.futures import ThreadPoolExecutor, ProcessPoolExecutor
def sleep_time(sleep_seconds: int) -> int:
time.sleep(sleep_seconds)
print(f'sleep {sleep_seconds} seconds')
return sleep_seconds
if __name__ == '__main__':
with ThreadPoolExecutor(max_workers=3) as executor:
result = executor.map(sleep_time, sleep_list)
print(result)
print(list(result))
with ProcessPoolExecutor(max_workers=3) as executor:
result = executor.map(sleep_time, sleep_list)
print(result)
print(list(result))
|
这应该是 php 层面的工作 |
目标
引入多线程运行模式,使
Swoole
实现多线程+协程的运行方式。背景
在
Swoole
服务器编程开发中,协程的出现已经解决了大部分难题,但是我们发现跨进程读写数据依然很难,需要借助进程间通信(IPC
)、Redis
、Swoole\Table
或其他共享内存实现。Redis
、IPC
进程间通信方式性能较差。而Swoole\Table
的问题是需要固定分配内存,无法扩容,存在诸多限制。除此之外,多进程的调试非常麻烦,例如我们要使用
gdb
就需要gdb -p
逐个进程去追踪,而Java
、Golang
这样的多线程模型,只有一个进程,调试更简单。实现一些底层的工具也会更容易。实现方式
PHP
的ZTS
机制,TSRM API
,在PHP
层面全局变量是完全隔离的,但底层C++
层面全局变量是可见的Event Worker
、Task Worker
、User Process
全部替换为Thread
Swoole\Thread\Map
和Swoole\Thread\List
实现跨线程的数据读写创建新线程时,隔离全局变量
创建线程
由于
ZTS
的机制,实际上Swoole\Thread
与Swoole\Process
是一致的,无法共享任何对象资源。实际上
Thread::exec()
与Process::exec()
更接近,ZTS
线程反而比fork()
隔离得更为干净,fork()
是可以从父进程继承已创建的对象和资源,而ZTS
新的线程不会从父线程继承任何资源,相当于是一个全新的进程。虽然通过底层的技术手段可以实现线程之间传递对象和资源,例如
ext-pthreads
等扩展,但涉及到并行操作同一个文件句柄和内存指针等复杂的问题。再加上Swoole
的异步IO
和协程机制带来的复杂性。应用层代码正确地使用锁,同时兼顾性能和数据一致性是一件极其困难的事情,错误的使用方法导致严重的BUG
,因此Swoole
不考虑提供这方面的支持。在线程中创建协程
在线程中可以使用
Co\run
创建新的协程调度器,使用Co\go
创建新的协程。不同线程之间的协程无任何关联,包括Channel
也只能在当前线程中使用。运行结果
# index.php [thread-0] TheadId=140327446116480, mainThread=1, CG(compiled_filename)=(nil) [thread-1] TheadId=140327442708032, mainThread=0, CG(compiled_filename)=(nil) begin [thread-2] TheadId=140327434315328, mainThread=0, CG(compiled_filename)=(nil) begin
使用
ps aux
可以看到只有一个进程:ps aux|grep php htf 783550 0.2 0.0 171424 12028 pts/11 Sl+ 10:51 0:00 php index.php
Server
SWOOLE_BASE
基本相同,每个线程一个EventLoop
和协程调度器WorkerG
全局变量,使Worker
作为线程独立运行Server::start()
进入事件循环,监听Worker
进程事件Worker
都会执行一次SG(request_info).path_translated
,执行相同的代码,在Worker
线程中不执行任何destory/free
操作,该对象应为只读缺点
Crash
时或调用了Process::exit()
整个进程都会退出ZTS
和 锁的操作可能会额外的开销,性能可能会比NTS
多进程并发模型差10%
左右thread-context
线程 API
Thread::getId()
:获取当前线程的ID
Thread::getArguments()
:获取父线程传递给子线程的参数列表Thread::join()
等待子线程退出,请注意$thread
对象销毁时会自动执行join()
,这可能会导致进程阻塞Thread::joinable()
检测子线程是否已退出Thread::detach()
使子线程独立运行,不再需要Thread::join()
Thread::HARDWARE_CONCURRENCY
硬件层支持的并行线程数量Thread::$id
获取子线程的ID
并发 Map
并发 List
线程安全
Map
和ArrayList
在同一个进程的内存堆栈中,因此可以分配内存自由伸缩,不需要像Table
那样固定分配null/bool/int/float/string
类型,其他类型将在写入时自动序列化,读取时反序列化Map
和ArrayList
对象作为线程参数传递给子线程其他更新
Coroutine\MySQL
、Coroutine\Redis
、Coroutine\PostgreSQL
,已被pdo_mysql/mysqli
、ext-redis
、pdo_pgsql
替代The text was updated successfully, but these errors were encountered: