转-深度剖析 Swoole6 PHP 多线程实现原理
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"><span style="color: black;"><span style="color: black;">PHP</span><span style="color: black;">做为</span>一种常用的服务器端脚本语言,被广泛用于<span style="color: black;">Web</span><span style="color: black;">研发</span>。相比<span style="color: black;">Java</span>、<span style="color: black;">C++</span>、<span style="color: black;">Golang</span>等编程语言,<span style="color: black;">PHP</span>缺少多线程的支持,只能<span style="color: black;">运用</span><span style="color: black;">fork</span>创建多个进程来实现并行处理。 <span style="color: black;">因为</span>进程之间并不共享内存堆栈和文件句柄,<span style="color: black;">PHP</span>只能借助<span style="color: black;">Redis</span>或<span style="color: black;">APCu</span>等内存数据库或共享内存来实现进程间的数据共享,编程的局限性<span style="color: black;">很强</span>。</span></p>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"><span style="color: black;"><span style="color: black;">所幸的是</span><span style="color: black;">Swoole6</span><span style="color: black;">增多</span>了对多线程的支持,为<span style="color: black;">PHP</span><span style="color: black;">供给</span>了一个稳定<span style="color: black;">靠谱</span>的多线程支持。<span style="color: black;">此刻</span><span style="color: black;">PHP</span><span style="color: black;">亦</span><span style="color: black;">能够</span>创建多线程,更加<span style="color: black;">有效</span>地编写并发程序。</span></p>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"><span style="color: black;"><span style="color: black;">本文将深入介绍</span><span style="color: black;">PHP</span>的<span style="color: black;">ZTS</span>机制和<span style="color: black;">Swoole6</span>多线程的实现原理,<span style="color: black;">帮忙</span><span style="color: black;">PHP</span><span style="color: black;">研发</span>者彻底理解<span style="color: black;">把握</span><span style="color: black;">Swoole6</span>多线程的<span style="color: black;">运用</span>。</span></p>
<div style="color: black; text-align: left; margin-bottom: 10px;"><img src="https://p3-sign.toutiaoimg.com/tos-cn-i-axegupay5k/8297f237221d405fbc2b5233b7ebe166~noop.image?_iz=58558&from=article.pc_detail&lk3s=953192f4&x-expires=1728303062&x-signature=KToYuYVdDpVK03vtN2v3yDpBHfM%3D" style="width: 50%; margin-bottom: 20px;"></div>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"><strong style="color: blue;"><span style="color: black;"><span style="color: black;">进程与线程的对比</span></span></strong></p>
<div style="color: black; text-align: left; margin-bottom: 10px;"><img src="https://p3-sign.toutiaoimg.com/tos-cn-i-6w9my0ksvp/65858d7de11c46cd88905f4d63a6cb82~noop.image?_iz=58558&from=article.pc_detail&lk3s=953192f4&x-expires=1728303062&x-signature=sOrZLbEu%2F9PBYXp8sxUPc2hVg5Y%3D" style="width: 50%; margin-bottom: 20px;"></div>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"><strong style="color: blue;"><span style="color: black;"><span style="color: black;"><span style="color: black;">怎样</span>创建线程</span></span></strong></p>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"><span style="color: black;"><span style="color: black;">在 </span><span style="color: black;">Linux C++</span> 中,<span style="color: black;">能够</span><span style="color: black;">运用</span>多种方式来创建线程。最常用的<span style="color: black;">办法</span>是<span style="color: black;">运用</span><span style="color: black;">POSIX 线程</span>(<span style="color: black;">pthread</span>)库。以下是<span style="color: black;">经过</span> <span style="color: black;">pthread</span> 库创建线程的基本示例。</span></p><span style="color: black;">#<span style="color: black;">include</span> <span style="color: black;"><pthread.h></span></span>
<span style="color: black;">#<span style="color: black;">include</span> <span style="color: black;"><iostream></span></span>
<span style="color: black;"><span style="color: black;">void</span>* <span style="color: black;">threadFunction</span><span style="color: black;">(<span style="color: black;">void</span>* arg)</span> </span>{
<span style="color: black;">int</span>* num =<span style="color: black;">static_cast</span><<span style="color: black;">int</span>*>(arg);
<span style="color: black;">std</span>::<span style="color: black;">cout</span> << <span style="color: black;">"Hello from thread! Number: "</span> << *num << <span style="color: black;">std</span>::<span style="color: black;">endl</span>;
<span style="color: black;">return</span> <span style="color: black;">nullptr</span>;
}
<span style="color: black;"><span style="color: black;">int</span> <span style="color: black;">main</span><span style="color: black;">()</span> </span>{
<span style="color: black;">pthread_t</span> thread;
<span style="color: black;">int</span> num = <span style="color: black;">42</span>;
<span style="color: black;">// 创建线程</span>
<span style="color: black;">if</span> (pthread_create(&thread, <span style="color: black;">nullptr</span>, threadFunction, &num) != <span style="color: black;">0</span>) {
<span style="color: black;">std</span>::<span style="color: black;">cerr</span> << <span style="color: black;">"Error creating thread"</span> << <span style="color: black;">std</span>::<span style="color: black;">endl</span>;
<span style="color: black;">return</span> <span style="color: black;">1</span>;
}
<span style="color: black;">// 等待线程结束</span>pthread_join(thread,<span style="color: black;">nullptr</span>);
<span style="color: black;">std</span>::<span style="color: black;">cout</span> << <span style="color: black;">"Main thread ending."</span> << <span style="color: black;">std</span>::<span style="color: black;">endl</span>;
<span style="color: black;">return</span> <span style="color: black;">0</span>;
}<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"><span style="color: black;"><span style="color: black;"><span style="color: black;">能够</span><span style="color: black;">运用</span> </span><span style="color: black;">g++</span> 编译器编译此代码,<span style="color: black;">而后</span>执行就会创建多个线程并行地处理任务。</span></p>g++ -o <span style="color: black;">test</span> test.cpp -lpthread
./<span style="color: black;">test</span>
<h1 style="color: black; text-align: left; margin-bottom: 10px;">PHP ZTS</h1>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"><span style="color: black;"><span style="color: black;"><span style="color: black;">初期</span>的</span><span style="color: black;">PHP</span>仅支持<span style="color: black;">Apache</span>服务器,<span style="color: black;">做为</span><span style="color: black;">Apache</span>的<span style="color: black;">prefork</span>模块来运行,不支持<span style="color: black;">Windows</span>的<span style="color: black;">IIS</span>和<span style="color: black;">Apache (worker threads)</span>服务器。为<span style="color: black;">认识</span>决此问题,<span style="color: black;">PHP</span>加入了<span style="color: black;">ZTS</span>的支持,<span style="color: black;">亦</span><span style="color: black;">便是</span><span style="color: black;">TSRM</span>模块,<span style="color: black;">能够</span>在<span style="color: black;">php-src/TSRM</span>目录下找到相应的代码。</span></p>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"><span style="color: black;"><span style="color: black;">与</span><span style="color: black;">Python GIL</span>的实现<span style="color: black;">区别</span>,<span style="color: black;">PHP ZTS</span><span style="color: black;">无</span><span style="color: black;">运用</span>全局锁<span style="color: black;">守护</span>全局资源,而是一种<span style="color: black;">thread_local</span>的模式,将全局资源变<span style="color: black;">成为了</span>线程局部资源。</span></p>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"><span style="color: black;"><span style="color: black;">Python</span>语言虽然<span style="color: black;">供给</span>了<span style="color: black;">threading</span>模块,<span style="color: black;">实质</span>是伪多线程,<span style="color: black;">Python</span>代码并<span style="color: black;">不可</span>并行执行,仅在<span style="color: black;">出现</span>阻塞<span style="color: black;">IO</span>时,让出了<span style="color: black;">掌控</span>权,利用<span style="color: black;">IO</span>等待的间隙,运行其他<span style="color: black;">Python</span>线程。而<span style="color: black;">PHP ZTS</span>多线程模式(例如:<span style="color: black;">IIS+PHP</span>)下,<span style="color: black;">PHP</span> 程序是并行执行的,但并<span style="color: black;">不可</span>读取到当前线程以外的资源。</span></p>
<h1 style="color: black; text-align: left; margin-bottom: 10px;">PHP 底层的全局变量</h1>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"><span style="color: black;"><span style="color: black;">在 </span><span style="color: black;">PHP</span> 的 <span style="color: black;">Zend</span> 引擎中,有<span style="color: black;">有些</span>全局变量和结构体用于存储运行时的状态和<span style="color: black;">关联</span>信息。以下是<span style="color: black;">有些</span><span style="color: black;">平常</span>的全局变量,如 <span style="color: black;">EG</span>、<span style="color: black;">PG</span> 和 <span style="color: black;">CG</span>:</span></p>
<h1 style="color: black; text-align: left; margin-bottom: 10px;">AG ZendVM 内存管理器</h1>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"><span style="color: black;"><span style="color: black;">AG</span> <span style="color: black;">保留</span>了内存管理器<span style="color: black;">关联</span>的资源,<span style="color: black;">重点</span>的结构是:<span style="color: black;">zend_mm_heap *mm_heap</span>,<span style="color: black;">PHP</span> 所有变量的内存分配<span style="color: black;">所有</span>由<span style="color: black;">zend_mm_alloc_heap(AG(mm_heap), size, ...)</span>函数所实现。</span></p>
<h1 style="color: black; text-align: left; margin-bottom: 10px;">GC_G ZendVM 垃圾回收器</h1>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"><span style="color: black;"><span style="color: black;">GC_G</span>是垃圾回收器对象,<span style="color: black;">经过</span>引用计数和循环引用分析、写时复制 (Copy-on-Write) 垃圾回收算法进行<span style="color: black;">PHP</span>变量的生命周期管理和资源回收。</span></p>
<h1 style="color: black; text-align: left; margin-bottom: 10px;">EG (Executor Globals)</h1>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"><span style="color: black;"><span style="color: black;">EG</span> 是一个指向 <span style="color: black;">executor_globals</span> 结构的指针,<span style="color: black;">包括</span>了执行器的全局状态信息,<span style="color: black;">包含</span>当前执行的上下文、错误处理、安全上下文等。</span></p>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"><span style="color: black;"><span style="color: black;"><span style="color: black;">重点</span>字段:</span></span></p><span style="color: black;">• </span><span style="color: black;">current_execute_data</span>: 指向当前正在执行的函数调用的执行数据结构<span style="color: black;">• </span><span style="color: black;">active_symbol_table</span>: 当前活动的符号表,用于存储变量及其值<span style="color: black;">• </span><span style="color: black;">HashTable *function_table</span>:函数表<span style="color: black;">• </span><span style="color: black;">HashTable *class_table</span>:类表<span style="color: black;">• </span><span style="color: black;">zend_object *exception</span>:运行时的<span style="color: black;">反常</span><span style="color: black;">• </span><span style="color: black;">zend_vm_stack vm_stack</span>:运行的函数调用栈<h1 style="color: black; text-align: left; margin-bottom: 10px;">PG (Persistent Globals)</h1>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"><span style="color: black;"><span style="color: black;">PG</span> 是一个指向 <span style="color: black;">persistent_globals</span> 结构的指针,<span style="color: black;">包括</span>了持久化(跨请求)全局状态信息,<span style="color: black;">重点</span>用于存储在请求之间保持不变的数据。</span></p>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"><span style="color: black;"><span style="color: black;"><span style="color: black;">重点</span>字段:</span></span></p><span style="color: black;">• </span><span style="color: black;">auto_prepend_file</span>: 自动<span style="color: black;">包括</span>在脚本执行前的文件<span style="color: black;">• </span><span style="color: black;">auto_append_file</span>: 自动<span style="color: black;">包括</span>在脚本执行后的文件<span style="color: black;">• </span><span style="color: black;">display_errors</span>: <span style="color: black;">掌控</span><span style="color: black;">是不是</span><span style="color: black;">表示</span>错误的配置选项<h1 style="color: black; text-align: left; margin-bottom: 10px;">CG (Compiler Globals)</h1>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"><span style="color: black;"><span style="color: black;">CG</span> 是一个指向 <span style="color: black;">compiler_globals</span>结构的指针,<span style="color: black;">包括</span>了与编译<span style="color: black;">关联</span>的全局状态和信息,在<span style="color: black;">PHP</span> 代码的编译<span style="color: black;">周期</span><span style="color: black;">运用</span>。</span></p>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"><span style="color: black;"><span style="color: black;"><span style="color: black;">重点</span>字段:</span></span></p><span style="color: black;">• </span><span style="color: black;">compiler_options</span>: 编译选项的配置<span style="color: black;">• </span><span style="color: black;">active_symbol_table</span>: 当前编译<span style="color: black;">周期</span>的活动符号表<span style="color: black;">• </span><span style="color: black;">open_files</span>:当前打开的文件列表<h1 style="color: black; text-align: left; margin-bottom: 10px;">SG (SAPI Globals)</h1>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"><span style="color: black;"><span style="color: black;">SG</span>是一个用于存储与当前脚本执行<span style="color: black;">关联</span>的全局变量的结构。它<span style="color: black;">重点</span>用于管理与当前请求或执行上下文<span style="color: black;">关联</span>的信息。</span></p>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"><span style="color: black;"><span style="color: black;"><span style="color: black;">重点</span>字段:</span></span></p><span style="color: black;">• </span><strong style="color: blue;"><span style="color: black;">request_info</span></strong>:<span style="color: black;">包括</span>与当前请求<span style="color: black;">关联</span>的信息,例如请求的 <span style="color: black;">URI</span> 和<span style="color: black;">办法</span>等。<span style="color: black;">• </span><strong style="color: blue;"><span style="color: black;">sapi_headers</span></strong>:当前<span style="color: black;">HTTP Headers</span><span style="color: black;">• </span><strong style="color: blue;"><span style="color: black;">rfc1867_uploaded_files</span></strong>:当前上传的文件列表<h1 style="color: black; text-align: left; margin-bottom: 10px;">其他扩展的全局变量</h1>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"><span style="color: black;"><span style="color: black;">除了</span><span style="color: black;">ZendVM</span>之外,加载的<span style="color: black;">每一个</span>扩展可能都<span style="color: black;">运用</span>全局变量<span style="color: black;">保留</span>了数据,例如:</span></p><span style="color: black;">• </span><strong style="color: blue;"><span style="color: black;">BCG</span></strong>:<span style="color: black;">bcmath</span><span style="color: black;">• </span><strong style="color: blue;"><span style="color: black;">MYSQLND_G</span></strong>:<span style="color: black;">mysqlnd</span>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"><span style="color: black;"><span style="color: black;">在</span><span style="color: black;">php-src</span>中<span style="color: black;">运用</span><span style="color: black;">ZEND_BEGIN_MODULE_GLOBALS</span>定义全局变量。</span></p>ZEND_BEGIN_MODULE_GLOBALS(gmp)
<span style="color: black;">bool</span> rand_initialized;
<span style="color: black;">gmp_randstate_t</span> rand_state;
ZEND_END_MODULE_GLOBALS(gmp)<h1 style="color: black; text-align: left; margin-bottom: 10px;">TSRM 介绍</h1>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"><span style="color: black;"><span style="color: black;">TSRM</span>(<span style="color: black;">Thread Safe Resource Management</span>)是 <span style="color: black;">PHP</span>中的一种机制,旨在为多线程环境<span style="color: black;">供给</span>资源管理的线程安全支持。它<span style="color: black;">准许</span>多个线程安全地<span style="color: black;">拜访</span>和操作共享资源,<span style="color: black;">保证</span>在并发执行时不会<span style="color: black;">出现</span>数据竞争或状态不一致的问题。</span></p>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"><span style="color: black;"><span style="color: black;">TSRM</span>由编译参数<span style="color: black;">掌控</span>,<span style="color: black;">因此呢</span><span style="color: black;">是不是</span>开启<span style="color: black;">ZTS</span>决定于<span style="color: black;">php-src</span>编译时的选项。<span style="color: black;">增多</span><span style="color: black;">--enable-zts</span>就<span style="color: black;">能够</span>开启<span style="color: black;">ZTS</span>。</span></p>
<h1 style="color: black; text-align: left; margin-bottom: 10px;">NTS</h1>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"><span style="color: black;"><span style="color: black;">以</span><span style="color: black;">AG</span>为例,在<span style="color: black;">NTS</span>下<span style="color: black;">AG(mm_heap)</span>宏展开后是:<span style="color: black;">alloc_globals.mm_heap</span>,<span style="color: black;">实质</span>定义是</span></p><span style="color: black;">static</span>zend_alloc_globals alloc_globals;<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"><span style="color: black;"><span style="color: black;">即进程全局变量,此全局变量<span style="color: black;">保留</span>了所有内存分配器的资源。</span></span></p>
<h1 style="color: black; text-align: left; margin-bottom: 10px;">ZTS</h1>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"><span style="color: black;"><span style="color: black;">在</span><span style="color: black;">ZTS</span>下宏展开后<span style="color: black;">实质</span>的符号是:</span></p>(<span style="color: black;">(<span style="color: black;">(zend_alloc_globals *)</span> ((<span style="color: black;">(char*)</span> tsrm_get_ls_cache<span style="color: black;">()</span>)+<span style="color: black;">(alloc_globals_offset)</span>))-></span>mm_heap)<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"><span style="color: black;"><span style="color: black;">而</span><span style="color: black;">tsrm_get_ls_cache()</span>函数<span style="color: black;">便是</span>获取一个<span style="color: black;">Thread Local</span>变量,在<span style="color: black;">Linux</span>系统下<span style="color: black;">运用</span>了<span style="color: black;">pthread_getspecific()</span>实现。</span></p>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"><span style="color: black;"><span style="color: black;">pthread_getspecific</span> 是 <span style="color: black;">POSIX</span>线程库中的一个函数,用于在多线程程序中<span style="color: black;">拜访</span>与特定线程<span style="color: black;">关联</span>的线程局部存储(<span style="color: black;">Thread Local Storage</span>, <span style="color: black;">TLS</span>)数据。该函数<span style="color: black;">准许</span>线程获取已存储的特定数据指针,这些指针是在先前<span style="color: black;">经过</span> <span style="color: black;">pthread_setspecific</span> 存储的。</span></p>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"><span style="color: black;"><span style="color: black;"><span style="color: black;">另一</span>一个关键的函数是</span><span style="color: black;">ts_resource_ex()</span>,在线程创建时分配内存,调用<span style="color: black;">pthread_setspecific</span>设置为<span style="color: black;">TLS</span>指针。</span></p><span style="color: black;">/* fetches the requested resource for the current thread */</span>
<span style="color: black;">TSRM_API</span> <span style="color: black;">void</span> *<span style="color: black;">ts_resource_ex</span>(ts_rsrc_id id, THREAD_T *th_id) {
...
<span style="color: black;">if</span>(!thread_resources) {<span style="color: black;">allocate_new_resource</span>(&tsrm_tls_table, thread_id);
<span style="color: black;">tsrm_mutex_unlock</span>(tsmm_mutex);
<span style="color: black;">return</span> <span style="color: black;">ts_resource_ex</span>(id, &thread_id);
}
}
<h1 style="color: black; text-align: left; margin-bottom: 10px;">总结</h1>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"><span style="color: black;"><span style="color: black;">这些全局资源和<span style="color: black;">关联</span>的<span style="color: black;">规律</span>构<span style="color: black;">成为了</span></span><span style="color: black;">ZendVM</span>,在<span style="color: black;">ZTS</span>模式下,底层的全局变量被编译为了<span style="color: black;">TLS</span>线程局部变量。这就相当于<strong style="color: blue;"><span style="color: black;"><span style="color: black;">每一个</span>线程都有一个独立的</span><span style="color: black;">ZendVM</span>环境,彼此是隔离的</strong>。<span style="color: black;">因此呢</span><span style="color: black;">ZTS</span>模式下,即便在同一个线程内,<span style="color: black;">实质</span>上程序中创建的全局变量或资源,例如:<span style="color: black;">$_GET/$_POST/$_FILES</span>或其他<span style="color: black;">运用</span><span style="color: black;">global $vars</span>,以及<span style="color: black;">include $file</span>等均为<span style="color: black;">TLS</span>资源,只能在当前线程内<span style="color: black;">运用</span>。</span></p>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"><span style="color: black;"><span style="color: black;">这相当于是</span><span style="color: black;">PHP</span>层面,线程变<span style="color: black;">成为了</span>进程,但在底层视角(<span style="color: black;">C/C++</span>)仍然是共享堆栈的线程环境。</span></p>
<h1 style="color: black; text-align: left; margin-bottom: 10px;">Swoole6 线程</h1>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"><span style="color: black;"><span style="color: black;"><span style="color: black;">因为</span></span><span style="color: black;">Swoole</span><span style="color: black;">运用</span>了<span style="color: black;">C++11</span>,<span style="color: black;">因此呢</span><span style="color: black;">能够</span>直接<span style="color: black;">运用</span><span style="color: black;">C++</span>标准的多线程支持,而不是直接<span style="color: black;">运用</span><span style="color: black;">pthread</span>底层库。</span></p>
<h1 style="color: black; text-align: left; margin-bottom: 10px;">创建线程</h1><span style="color: black;"><span style="color: black;">static</span> <span style="color: black;">PHP_METHOD</span><span style="color: black;">(swoole_thread, __construct)</span> </span>{
<span style="color: black;">char</span> *script_file;
<span style="color: black;">size_t</span> l_script_file;
zval *args;
<span style="color: black;">int</span> argc;
ZendArray *argv = <span style="color: black;">nullptr</span>;
ZEND_PARSE_PARAMETERS_START(<span style="color: black;">1</span>, <span style="color: black;">-1</span>)
Z_PARAM_STRING(script_file, l_script_file)
Z_PARAM_VARIADIC(<span style="color: black;">+</span>, args, argc)
ZEND_PARSE_PARAMETERS_END();<span style="color: black;">if</span> (l_script_file < <span style="color: black;">1</span>) {
zend_throw_exception(swoole_exception_ce, <span style="color: black;">"exec file name is empty"</span>, SW_ERROR_INVALID_PARAMS);
<span style="color: black;">return</span>;
}
ThreadObject *to = thread_fetch_object(Z_OBJ_P(ZEND_THIS));
zend_string *file = zend_string_init(script_file, l_script_file,<span style="color: black;">1</span>);
<span style="color: black;">if</span> (argc > <span style="color: black;">0</span>) {
argv = <span style="color: black;">new</span>ZendArray();<span style="color: black;">for</span> (<span style="color: black;">int</span> i = <span style="color: black;">0</span>; i < argc; i++) {
argv->append(&args);
}
}
<span style="color: black;">try</span> {
to->thread = <span style="color: black;">new</span> <span style="color: black;">std</span>::thread(() { php_swoole_thread_start(file, argv); });
}<span style="color: black;">catch</span> (<span style="color: black;">const</span> <span style="color: black;">std</span>::exception &e) {
zend_throw_exception(swoole_exception_ce, e.what(), SW_ERROR_SYSTEM_CALL_FAIL);
<span style="color: black;">return</span>;
}
zend_update_property_long(
swoole_thread_ce, SW_Z8_OBJ_P(ZEND_THIS), ZEND_STRL(<span style="color: black;">"id"</span>), (zend_long) to->thread->native_handle());
}<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"><span style="color: black;"><span style="color: black;">底层直接<span style="color: black;">运用</span>了</span><span style="color: black;">C++</span>的<span style="color: black;">std::thread</span>创建线程,子线程会执行<span style="color: black;">php_swoole_thread_start()</span>函数初始化子线程。</span></p>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"><span style="color: black;"><span style="color: black;">构造<span style="color: black;">办法</span>接受</span><span style="color: black;">2</span>个参数,<span style="color: black;">第1</span>个是子线程要执行的脚本文件,第二个是线程参数数组。</span></p>
<h1 style="color: black; text-align: left; margin-bottom: 10px;">线程初始化</h1><span style="color: black;">void</span> <span style="color: black;">php_swoole_thread_start(zend_string</span> <span style="color: black;">*file,</span> <span style="color: black;">ZendArray</span> <span style="color: black;">*argv)</span> <span style="color: black;">{</span>
<span style="color: black;">thread_num.fetch_add(1);</span>
<span style="color: black;">ts_resource(0);</span>
<span style="color: black;">#if defined(COMPILE_DL_SWOOLE) && defined(ZTS)</span>
<span style="color: black;">ZEND_TSRMLS_CACHE_UPDATE();</span>
<span style="color: black;">#endif</span>
<span style="color: black;">zend_file_handle</span> <span style="color: black;">file_handle{};</span>
<span style="color: black;">zval</span> <span style="color: black;">global_argc,</span> <span style="color: black;">global_argv;</span>
<span style="color: black;">PG(expose_php)</span> <span style="color: black;">=</span> <span style="color: black;">0</span><span style="color: black;">;</span>
<span style="color: black;">PG(auto_globals_jit)</span> <span style="color: black;">=</span> <span style="color: black;">1</span><span style="color: black;">;</span>
<span style="color: black;">#if PHP_VERSION_ID >= 80100</span>
<span style="color: black;">PG(enable_dl)</span> <span style="color: black;">=</span> <span style="color: black;">false</span><span style="color: black;">;</span>
<span style="color: black;">#else</span>
<span style="color: black;">PG(enable_dl)</span> <span style="color: black;">=</span> <span style="color: black;">0</span><span style="color: black;">;</span>
<span style="color: black;">#endif</span>
<span style="color: black;">swoole_thread_init();</span>
<span style="color: black;">if</span> <span style="color: black;">(php_request_startup()</span> <span style="color: black;">!=</span> <span style="color: black;">SUCCESS)</span> <span style="color: black;">{</span>
<span style="color: black;">EG(exit_status)</span> <span style="color: black;">=</span> <span style="color: black;">1</span><span style="color: black;">;</span>
<span style="color: black;">goto</span> <span style="color: black;">_startup_error;</span>
<span style="color: black;">}</span>
<span style="color: black;">PG(during_request_startup)</span> <span style="color: black;">=</span> <span style="color: black;">0</span><span style="color: black;">;</span>
<span style="color: black;">SG(sapi_started)</span> <span style="color: black;">=</span> <span style="color: black;">0</span><span style="color: black;">;</span>
<span style="color: black;">SG(headers_sent)</span> <span style="color: black;">=</span> <span style="color: black;">1</span><span style="color: black;">;</span>
<span style="color: black;">SG(request_info).no_headers</span> <span style="color: black;">=</span> <span style="color: black;">1</span><span style="color: black;">;</span>
<span style="color: black;">SG(request_info).path_translated</span> <span style="color: black;">=</span> <span style="color: black;">request_info.path_translated;</span>
<span style="color: black;">SG(request_info).argc</span> <span style="color: black;">=</span> <span style="color: black;">request_info.argc;</span>
<span style="color: black;">zend_stream_init_filename(&file_handle,</span> <span style="color: black;">ZSTR_VAL(file));</span>
<span style="color: black;">file_handle.primary_script</span> <span style="color: black;">=</span> <span style="color: black;">1</span><span style="color: black;">;</span>
<span style="color: black;">zend_first_try</span> <span style="color: black;">{</span>
<span style="color: black;">thread_bailout</span> <span style="color: black;">=</span> <span style="color: black;">EG(bailout);</span>
<span style="color: black;">if</span> <span style="color: black;">(request_info.argv_serialized)</span> <span style="color: black;">{</span>
<span style="color: black;">php_swoole_unserialize(request_info.argv_serialized,</span> <span style="color: black;">&global_argv);</span>
<span style="color: black;">ZVAL_LONG(&global_argc,</span> <span style="color: black;">request_info.argc);</span>
<span style="color: black;">zend_hash_update(&EG(symbol_table),</span> <span style="color: black;">ZSTR_KNOWN(ZEND_STR_ARGV),</span> <span style="color: black;">&global_argv);</span>
<span style="color: black;">zend_hash_update(&EG(symbol_table),</span> <span style="color: black;">ZSTR_KNOWN(ZEND_STR_ARGC),</span> <span style="color: black;">&global_argc);</span>
<span style="color: black;">}</span>
<span style="color: black;">if</span> <span style="color: black;">(argv)</span> <span style="color: black;">{</span>
<span style="color: black;">argv->toArray(&thread_argv);</span>
<span style="color: black;">argv->del_ref();</span>
<span style="color: black;">}</span>
<span style="color: black;">php_swoole_thread_register_stdio_file_handles(true);</span>
<span style="color: black;">php_execute_script(&file_handle);</span>
<span style="color: black;">}</span>
<span style="color: black;">zend_end_try();</span>
<span style="color: black;">zend_destroy_file_handle(&file_handle);</span>
<span style="color: black;">php_request_shutdown(NULL);</span>
<span style="color: black;">file_handle.filename</span> <span style="color: black;">=</span> <span style="color: black;">NULL</span><span style="color: black;">;</span>
<span style="color: black;">_startup_error:</span>
<span style="color: black;">zend_string_release(file);</span>
<span style="color: black;">ts_free_thread();</span>
<span style="color: black;">swoole_thread_clean();</span>
<span style="color: black;">thread_num.fetch_sub(1);</span>
<span style="color: black;">}</span>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"><span style="color: black;"><span style="color: black;">关键的几个流程:</span></span></p><span style="color: black;">• </span><span style="color: black;">ts_resource</span> <span style="color: black;">运用</span> <span style="color: black;">TSRM API</span> 分配了 <span style="color: black;">TLS</span> 资源<span style="color: black;">• </span><span style="color: black;">php_request_startup</span> 在子线程内执行 <span style="color: black;">RINIT</span> ,这会调用所有扩展的<span style="color: black;">RINIT</span>函数<span style="color: black;">• </span><span style="color: black;">php_execute_script</span> 在子线程内执行<span style="color: black;">PHP</span>脚本<span style="color: black;">• </span><span style="color: black;">php_request_shutdown</span> 执行<span style="color: black;">RSHUTDOWN</span>函数<span style="color: black;">• </span><span style="color: black;">ts_free_thread</span> <span style="color: black;">运用</span> <span style="color: black;">TSRM API</span> 释放 <span style="color: black;">TLS</span> 资源<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"><span style="color: black;"><span style="color: black;">线程结束后,会调用</span><span style="color: black;">std::thread</span>的<span style="color: black;">join()</span><span style="color: black;">办法</span>回收线程。</span></p>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"><span style="color: black;"><span style="color: black;">线程创建的线程就<span style="color: black;">能够</span>并行地执行了,但<span style="color: black;">每一个</span>线程彼此是完全隔离的,这和多进程并<span style="color: black;">无</span>区别。接下来就需要实现线程资源的共享。</span></span></p>
<h1 style="color: black; text-align: left; margin-bottom: 10px;">ThreadResource</h1>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"><span style="color: black;"><span style="color: black;">Swoole</span>底层封装了<span style="color: black;">ThreadResource</span>来管理跨线程的共享资源。这个类<span style="color: black;">运用</span>引用计数来管理内存。底层<span style="color: black;">运用</span>了<span style="color: black;">atomic</span>来<span style="color: black;">增多</span>、减少引用计数,<span style="color: black;">因此呢</span>不需要加锁。当<span style="color: black;">无</span>任何线程持有此资源时就会执行<span style="color: black;">delete</span>释放对象。</span></p><span style="color: black;"><span style="color: black;">class</span> <span style="color: black;">ThreadResource</span> {</span>
<span style="color: black;">sw_atomic_t</span> ref_count;
<span style="color: black;">public</span>:
ThreadResource() {
ref_count = <span style="color: black;">1</span>;
}
<span style="color: black;"><span style="color: black;">void</span> <span style="color: black;">add_ref</span><span style="color: black;">()</span> </span>{
sw_atomic_add_fetch(&ref_count, <span style="color: black;">1</span>);
}
<span style="color: black;"><span style="color: black;">void</span> <span style="color: black;">del_ref</span><span style="color: black;">()</span> </span>{
<span style="color: black;">if</span>(sw_atomic_sub_fetch(&ref_count,<span style="color: black;">1</span>) == <span style="color: black;">0</span>) {
<span style="color: black;">delete</span> <span style="color: black;">this</span>;
}
}
<span style="color: black;">protected</span>:
<span style="color: black;">virtual</span> ~ThreadResource() {}
};<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"><span style="color: black;"><span style="color: black;"><span style="color: black;">包含</span>以下对象,均继承了</span><span style="color: black;">ThreadResource</span>:</span></p><span style="color: black;">• </span><span style="color: black;">Swoole\Thread\Atomic</span><span style="color: black;">• </span><span style="color: black;">Swoole\Thread\Lock</span><span style="color: black;">• </span><span style="color: black;">Swoole\Thread\ArrayList</span><span style="color: black;">• </span><span style="color: black;">Swoole\Thread\Map</span><span style="color: black;">• </span><span style="color: black;">Swoole\Thread\Queue</span><span style="color: black;">• </span><span style="color: black;">Swoole\Thread\Barrier</span>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"><span style="color: black;"><span style="color: black;">这些对象<span style="color: black;">能够</span>安全地在线程之间传递。</span></span></p>
<h1 style="color: black; text-align: left; margin-bottom: 10px;">ZendArray</h1>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"><span style="color: black;"><span style="color: black;">ArrayList</span>和<span style="color: black;">Map</span><span style="color: black;">运用</span>了<span style="color: black;">ZendVM</span><span style="color: black;">供给</span>的<span style="color: black;">zend_array(persistent)</span>来实现,<span style="color: black;">因此呢</span>内存是直接由<span style="color: black;">glibc</span>和<span style="color: black;">malloc/free</span>管理。<span style="color: black;">针对</span>数组的操作底层<span style="color: black;">运用</span>了<span style="color: black;">RWLock</span>来防止竞争。</span></p><span style="color: black;"><span style="color: black;">class</span> <span style="color: black;">ZendArray</span> :</span> <span style="color: black;">public</span> ThreadResource {
<span style="color: black;">protected</span>:
swoole::RWLock lock_;
zend_array ht;<span style="color: black;"><span style="color: black;">static</span> <span style="color: black;">void</span> <span style="color: black;">item_dtor</span><span style="color: black;">(zval *pDest)</span> </span>{
ArrayItem *item = (ArrayItem *) Z_PTR_P(pDest);
<span style="color: black;">delete</span> item;
}
<span style="color: black;">public</span>:
ZendArray() : ThreadResource(), lock_(<span style="color: black;">0</span>) {
zend_hash_init(&ht, <span style="color: black;">0</span>, <span style="color: black;">NULL</span>, item_dtor, <span style="color: black;">1</span>);
}
~ZendArray() <span style="color: black;">override</span> {
zend_hash_destroy(&ht);
}
...
<span style="color: black;"><span style="color: black;">void</span> <span style="color: black;">strkey_offsetGet</span><span style="color: black;">(zval *zkey, zval *return_value)</span> </span>{
zend::<span style="color: black;">String <span style="color: black;">skey</span><span style="color: black;">(zkey)</span></span>;
lock_.lock_rd();
ArrayItem *item = (ArrayItem *) zend_hash_find_ptr(&ht, skey.get());<span style="color: black;">if</span> (item) {
item->fetch(return_value);
}
lock_.unlock();
}
<span style="color: black;"><span style="color: black;">void</span> <span style="color: black;">strkey_offsetSet</span><span style="color: black;">(zval *zkey, zval *zvalue)</span> </span>{
zend::<span style="color: black;">String <span style="color: black;">skey</span><span style="color: black;">(zkey)</span></span>;
<span style="color: black;">auto</span> item = <span style="color: black;">new</span>ArrayItem(zvalue);
item->setKey(skey);
lock_.lock();
zend_hash_update_ptr(&ht, item->key, item);
lock_.unlock();
}
...
}<span style="color: black;">• 读操作<span style="color: black;">运用</span></span><span style="color: black;">lock_rd()</span>共享锁,<span style="color: black;">因此呢</span><span style="color: black;">$map</span><span style="color: black;">这般</span>的操作,多线程并行执行时不会<span style="color: black;">显现</span>竞争<span style="color: black;">• 写操作<span style="color: black;">运用</span></span><span style="color: black;">lock()</span><span style="color: black;">独霸</span>锁,若多线程向同一个<span style="color: black;">$map</span>写入时会<span style="color: black;">显现</span>竞争<h1 style="color: black; text-align: left; margin-bottom: 10px;">ArrayItem</h1>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"><span style="color: black;"><span style="color: black;">所有写入线程数据容器的元素,均<span style="color: black;">运用</span>此类操作。</span></span></p><span style="color: black;">• 数值:例如</span><span style="color: black;">int</span>、<span style="color: black;">float</span>、<span style="color: black;">null</span>、<span style="color: black;">bool</span>,直接复制其值<span style="color: black;">• 字符串:需要完全复制字符串的内存</span><span style="color: black;">• </span><span style="color: black;">PHP</span>对象:需要序列化后,<span style="color: black;">做为</span>字符串存储,读取时再进行反序列化<span style="color: black;">• 资源:例如</span><span style="color: black;">php socket</span>、<span style="color: black;">php stream</span>、<span style="color: black;">swoole co_socket</span>需要进行<span style="color: black;">dup(fd)</span>对文件描述符<span style="color: black;">增多</span>一次引用计数,读取时再<span style="color: black;">增多</span>一次引用计数<span style="color: black;">• 线程资源:调用</span><span style="color: black;">ThreadResource::add_ref()</span><span style="color: black;">增多</span>引用计数,删除时减少引用计数<span style="color: black;">• 数组:转为</span><span style="color: black;">ArrayList</span>或<span style="color: black;">Map</span>对象<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"><span style="color: black;"><span style="color: black;">数据容器是支持嵌套结构的,例如</span><span style="color: black;">Map</span>中<span style="color: black;">能够</span>写入<span style="color: black;">ArrayList</span>,<span style="color: black;">ArrayList</span>中<span style="color: black;">能够</span>再添加一个<span style="color: black;">Queue</span>。</span></p>
<h1 style="color: black; text-align: left; margin-bottom: 10px;">线程参数</h1>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"><span style="color: black;"><span style="color: black;">线程参数本身是一个</span><span style="color: black;">ArrayList</span>对象,<span style="color: black;">经过</span>引用计数管理,在<span style="color: black;">区别</span>的线程之间传递。</span></p>
<h1 style="color: black; text-align: left; margin-bottom: 10px;">Queue</h1>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"><span style="color: black;"><span style="color: black;">Queue</span><span style="color: black;">运用</span>了<span style="color: black;">C++</span>的<span style="color: black;">std::queue</span>实现,它不仅是一个数据容器,还内置了线程<span style="color: black;">要求</span>变量(<span style="color: black;">std::condition_variable</span>),队列的消费者在队列为空时等待<span style="color: black;">要求</span>变量,生产者<span style="color: black;">push()</span>写入数据时<span style="color: black;">能够</span>唤醒队列的消费者。</span></p><span style="color: black;"><span style="color: black;">struct</span> <span style="color: black;">Queue</span> :</span> ThreadResource {
<span style="color: black;">std</span>::<span style="color: black;">queue</span><ArrayItem *> <span style="color: black;">queue</span>;
<span style="color: black;">std</span>::mutex lock_;
<span style="color: black;">std</span>::condition_variable cv_;
}<h1 style="color: black; text-align: left; margin-bottom: 10px;">等待</h1><span style="color: black;"><span style="color: black;">void</span> <span style="color: black;">pop_wait</span><span style="color: black;">(zval *return_value,<span style="color: black;">double</span> timeout)</span> </span>{
ArrayItem *item = <span style="color: black;">nullptr</span>;
<span style="color: black;">std</span>::unique_lock<<span style="color: black;">std</span>::mutex> _lock(lock_);
SW_LOOP {
<span style="color: black;">if</span> (!<span style="color: black;">queue</span>.empty()) {
item = <span style="color: black;">queue</span>.front();
<span style="color: black;">queue</span>.pop();
<span style="color: black;">break</span>;
} <span style="color: black;">else</span> {
<span style="color: black;">if</span> (timeout > <span style="color: black;">0</span>) {
<span style="color: black;">if</span> (cv_.wait_for(_lock, <span style="color: black;">std</span>::chrono::duration<<span style="color: black;">double</span>>(timeout)) == <span style="color: black;">std</span>::cv_status::timeout) {
<span style="color: black;">break</span>;
}
}<span style="color: black;">else</span> {
cv_.wait(_lock);
}
<span style="color: black;">// All threads have been awakened,</span>
<span style="color: black;">// but the data has already been acquired by other thread, returning NULL.</span>
<span style="color: black;">if</span> (<span style="color: black;">queue</span>.empty()) {
RETVAL_NULL();
swoole_set_last_error(SW_ERROR_NO_PAYLOAD);
<span style="color: black;">break</span>;
}
}
}
_lock.unlock();
<span style="color: black;">if</span>(item) {
item->fetch(return_value);<span style="color: black;">delete</span> item;
}
}<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"><span style="color: black;"><span style="color: black;"><span style="color: black;">这儿</span>有一个细节是队列弹出的元素转为</span><span style="color: black;">PHP</span>变量时,是在锁的同步区域之外,<span style="color: black;">原由</span>是<span style="color: black;">pop</span>之后仅当前的线程持有此元素,<span style="color: black;">能够</span>安全地进行操作,<span style="color: black;">因此</span>不需要加锁。</span></p>
<h1 style="color: black; text-align: left; margin-bottom: 10px;"><span style="color: black;">通告</span></h1><span style="color: black;"><span style="color: black;">void</span> <span style="color: black;">push_notify</span><span style="color: black;">(zval *zvalue, <span style="color: black;">bool</span>notify_all)</span> </span>{
<span style="color: black;">auto</span> item = <span style="color: black;">new</span> ArrayItem(zvalue);
<span style="color: black;">std</span>::unique_lock<<span style="color: black;">std</span>::mutex> _lock(lock_);
<span style="color: black;">queue</span>.push(item);
<span style="color: black;">if</span> (notify_all) {
cv_.notify_all();
} <span style="color: black;">else</span>{
cv_.notify_one();
}
}<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"><span style="color: black;"><span style="color: black;">调用了<span style="color: black;">要求</span>变量的</span><span style="color: black;">notify_one()/notify_all()</span><span style="color: black;">办法</span>唤醒<span style="color: black;">处在</span>等待状态的消费者线程。</span></p>
<h1 style="color: black; text-align: left; margin-bottom: 10px;">其他实现细节</h1>
<h1 style="color: black; text-align: left; margin-bottom: 10px;">1. 线程中的协程调度器</h1>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"><span style="color: black;"><span style="color: black;">在线程中<span style="color: black;">能够</span>创建协程调度器,底层实现直接<span style="color: black;">运用</span>了</span><span style="color: black;">C++</span>的<span style="color: black;">thread_local</span>关键词来隔离全局变量。<span style="color: black;">每一个</span>线程的协程和异步<span style="color: black;">IO</span>环境是隔离的。<span style="color: black;">包含</span>:</span></p><span style="color: black;">• </span><span style="color: black;">EventLoop</span><span style="color: black;">• </span><span style="color: black;">Coroutine Scheduler</span><span style="color: black;">• </span><span style="color: black;">Timer</span><span style="color: black;">• </span><span style="color: black;">Async Threads</span><span style="color: black;">• </span><span style="color: black;">Logger</span>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"><span style="color: black;"><span style="color: black;">相比</span><span style="color: black;">ZendVM</span>的<span style="color: black;">TLS</span>要简单<span style="color: black;">非常多</span>,可读性更高。</span></p>
<span style="color: black;">#<span style="color: black;">ifdef</span> SW_THREAD</span>
<span style="color: black;">#<span style="color: black;">define</span>SW_THREAD_LOCAL thread_local</span>
<span style="color: black;">extern</span> <span style="color: black;">std</span>::mutex sw_thread_lock;
<span style="color: black;">#<span style="color: black;">else</span></span>
<span style="color: black;">#<span style="color: black;">define</span> SW_THREAD_LOCAL</span>
<span style="color: black;">#<span style="color: black;">endif</span></span>
SW_THREAD_LOCAL <span style="color: black;">bool</span> PHPCoroutine::activated = <span style="color: black;">false</span>;
SW_THREAD_LOCAL zend_array *PHPCoroutine::options =<span style="color: black;">nullptr</span>;<h1 style="color: black; text-align: left; margin-bottom: 10px;">2. Server 的多线程模式</h1>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"><span style="color: black;"><span style="color: black;">多线程模式下将</span><span style="color: black;">Worker</span>进程、<span style="color: black;">Task</span>进程、<span style="color: black;">UserWorker</span>进程<span style="color: black;">所有</span>修改为线程的方式运行。<span style="color: black;">因为</span>线程模式下,<span style="color: black;">没法</span>复制线程的资源,需要在线程创建之后,重新创建一次。</span></p>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"><span style="color: black;"><span style="color: black;">工作线程是将<span style="color: black;">一样</span>的代码,再次执行一遍。例如 </span><span style="color: black;">new Server</span> 和 <span style="color: black;">Server::on()</span>,但<span style="color: black;">worker</span>线程不<span style="color: black;">准许</span>执行 <span style="color: black;">Server::set()</span> <span style="color: black;">办法</span>。在 <span style="color: black;">Server::start()</span> <span style="color: black;">办法</span>中,工作进程将进入 <span style="color: black;">worker_thread_fn()</span> 执行单元,而主线程则是创建线程,以及管理子线程,负责退出线程的重启和回收,以及<span style="color: black;">shutdown</span>。</span></p><span style="color: black;"><span style="color: black;">static</span> <span style="color: black;">PHP_METHOD</span><span style="color: black;">(swoole_server, start)</span> </span>{
zval *zserv = ZEND_THIS;
Server *serv = php_swoole_server_get_and_check_server(zserv);
<span style="color: black;">#<span style="color: black;">ifdef</span> SW_THREAD</span>
<span style="color: black;">if</span>(serv->is_worker_thread()) {
worker_thread_fn();
RETURN_TRUE;
}<span style="color: black;">#<span style="color: black;">endif</span></span>
<span style="color: black;">if</span> (serv->is_started()) {
php_swoole_fatal_error(
E_WARNING, <span style="color: black;">"server is running, unable to execute %s->start()"</span>, SW_Z_OBJCE_NAME_VAL_P(zserv));
RETURN_FALSE;
}
...
}<h1 style="color: black; text-align: left; margin-bottom: 10px;">3. AIO 线程池</h1>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"><span style="color: black;"><span style="color: black;">AIO</span>线程池是共享的,它是一个多对多的队列<span style="color: black;">MMCQ</span> (<span style="color: black;">Many To Many Concurrent Queue</span>),避免创建<span style="color: black;">太多</span><span style="color: black;">AIO</span>线程。</span></p>async_thread_lock.lock();
<span style="color: black;">if</span>(!async_thread_pool) {
async_thread_pool = std::make_shared<async::ThreadPool>(
SwooleG.aio_core_worker_num, SwooleG.aio_worker_num, SwooleG.aio_max_wait_time, SwooleG.aio_max_idle_time);
}<span style="color: black;">if</span>(!async_thread_pool->is_running()) {
async_thread_pool->start();
}
pool = async_thread_pool;
async_thread_lock.unlock();<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"><span style="color: black;"><span style="color: black;">需要为<span style="color: black;">每一个</span></span><span style="color: black;">PHP</span>线程创建一个独立的管道来获取<span style="color: black;">AIO</span>线程池的<span style="color: black;">通告</span>。</span></p><span style="color: black;"><span style="color: black;">class</span> <span style="color: black;">AsyncThreads</span> {</span>
<span style="color: black;">public</span>:
<span style="color: black;">size_t</span> task_num = <span style="color: black;">0</span>;
Pipe *pipe = <span style="color: black;">nullptr</span>;
<span style="color: black;">std</span>::<span style="color: black;">shared_ptr</span><async::ThreadPool> pool;
network::Socket *read_socket = <span style="color: black;">nullptr</span>;
network::Socket *write_socket =<span style="color: black;">nullptr</span>;
}<h1 style="color: black; text-align: left; margin-bottom: 10px;">结语</h1>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"><span style="color: black;"><span style="color: black;">Swoole v6</span>为<span style="color: black;">PHP</span><span style="color: black;">供给</span>了一个稳定<span style="color: black;">靠谱</span>的多线程<span style="color: black;">方法</span>。<span style="color: black;">Swoole</span>的核心仍然是协程,多线程的支持只是为了补齐了<span style="color: black;">Swoole</span>的最后<span style="color: black;">一起</span>短板,相比<span style="color: black;">APCu</span>和<span style="color: black;">Redis</span>,多线程在数据和资源共享有巨大的<span style="color: black;">优良</span>。</span></p>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"><span style="color: black;"><span style="color: black;">除了当前<span style="color: black;">供给</span>的数据容器之外,<span style="color: black;">将来</span></span><span style="color: black;">Swoole</span>会<span style="color: black;">连续</span><span style="color: black;">增多</span><span style="color: black;">更加多</span>高性能的多线程<span style="color: black;">C++</span>组件,<span style="color: black;">持续</span><span style="color: black;">加强</span>多线程支持。</span></p>
<h1 style="color: black; text-align: left; margin-bottom: 10px;">原文</h1>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;">https://mp.weixin.qq.com/s/HzPEg7g3PuN2Xky4EQfnHw</p>
感谢你的精彩评论,带给我新的思考角度。 我完全同意你的观点,说得太对了。 我深受你的启发,你的话语是我前进的动力。
页:
[1]