Closed matyhtf closed 5 months ago
Swoole v6 要来了!即将增加多线程支持 https://github.com/swoole/swoole-src/pull/5281/files https://github.com/swoole/swoole-src/tree/v6.0 https://github.com/swoole/swoole-src/commit/908644c72b2de0407e62a3d79f04f35fef09c98f https://cplusplus.com/reference/multithreading/
以后是不是可以写成类似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))
Swoole v6 要来了!即将增加多线程支持 https://github.com/swoole/swoole-src/pull/5281/files https://github.com/swoole/swoole-src/tree/v6.0 swoole/swoole-src@908644c https://cplusplus.com/reference/multithreading/
以后是不是可以写成类似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
也只能在当前线程中使用。运行结果
使用
ps aux
可以看到只有一个进程: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
替代