引入多线程运行模式,使 Swoole
实现多线程+协程的运行方式。
在 Swoole
服务器编程开发中,协程的出现已经解决了大部分难题,但是我们发现跨进程读写数据依然很难,需要借助 进程间通信、Redis、Swoole\Table 或其他共享内存实现。
Redis
、IPC
进程间通信方式性能较差。而 Swoole\Table
的问题是需要固定分配内存,无法扩容,存在诸多限制。
除此之外,多进程的调试非常麻烦,例如我们要使用 gdb 就需要 gdb -p
逐个进程去追踪,而 Java 、Golang 这样的多线程模型,只有一个进程,调试更简单。实现一些底层的工具也会更容易。
修改 swoole-cli
的 CLI SAPI
加入参数开启多线程,仅在 ZTS
模式下可用。
tsrm_startup(128, 1, 0, NULL);
std::thread newThread1([=]() {
ts_resource(1);
zend_first_try {
mt_debug("thread-1");
do_cli(argc, argv);
}
zend_end_try();
});
# index.php
<?php
echo "hello world\n";
sleep(10);
echo "end\n";
swoole-cli --threads=8 index.php
[thread-0] TheadId=140327446116480, mainThread=1, CG(compiled_filename)=(nil)
[thread-1] TheadId=140327442708032, mainThread=0, CG(compiled_filename)=(nil)
hello world
[thread-2] TheadId=140327434315328, mainThread=0, CG(compiled_filename)=(nil)
hello world
使用 ps aux
可以看到只有一个进程:
ps aux|grep swoole-cli
htf 783550 0.2 0.0 171424 12028 pts/11 Sl+ 10:51 0:00 swoole-cli --threads=8 index.php
- 去掉进程管理器,运行方式与
SWOOLE_BASE
基本相同,每个线程一个 EventLoop 和协程调度器 - 隔离 WorkerG 全局变量,使 Worker 作为线程独立运行
new Server
时需要加锁,避免重复创建Server::start()
进入事件循环,监听Worker
进程事件
- 发生
Fatal Error
或Crash
整个进程都会退出 ZTS
和 锁的操作可能会额外的开销- 不支持
thread-context
<?php
use Swoole\Thread;
# 防止并发
Thread::once(function () {
echo "hello world";
});
# 获取线程 ID
echo Thread::id();
# 相当于 C++ 的 std::unorderedmap<long, std::string>
$map1 = Thread::map('global_map_1', Thread::TYPE_INT, Thread::TYPE_STRING);
# 相当于 C++ 的 std::unorderedmap<std::string, double>
$map2 = Thread::map('global_map_2', Thread::TYPE_STRING, Thread::TYPE_FLOAT);
# 相当于 C++ 的 std::vector<long>
$list = Thread::list('global_list_1', Thread::TYPE_INT);
Thread::once()
可以让一个函数只运行一次Thread::map()
创建全局HashTable
,Thread::list()
创建全局列表,底层会自动加锁,是线程安全的- 支持
int64/double/string
3
种格式
- 移除
Coroutine\MySQL
、Coroutine\Redis
、Coroutine\PostgreSQL
,已被pdo_mysql/mysqli
、ext-redis
、pdo_pgsql
替代