pthreads
A fair amount has changed for the pthreads extension with the release of pthreads v3. This article aims to cover the necessary information for those who are looking to upgrade their applications from pthreads v2 to v3.
一个公平的数额已经改变了并行线程扩展与并行线程V3的释放。 本文旨在为那些希望将其应用程序从pthreads v2升级到v3的用户提供必要的信息。
If you’re unfamiliar with pthreads, check out my introduction to pthreads instead!
如果您不熟悉pthread,请查看我对pthread的介绍 !
A big thank you to Joe Watkins for proofreading and helping to improve my article!
非常感谢Joe Watkins校对并帮助改进了我的文章!
通用变更 (Generic Changes)
There have been a few general changes made in pthreads v3. The first, and perhaps most prominent, is that pthreads cannot be used in any environment other than the command line interface. It was never meant to be used in a web server environment (i.e. in an FCGI process) due to safety and scaling concerns, so the advice from pthreads v2 has now been enforced.
pthreads v3中进行了一些常规更改。 第一个,也许是最突出的一点是,pthreads 不能在命令行界面以外的任何环境中使用。 由于安全和扩展方面的考虑,它从未打算在Web服务器环境(即FCGI进程)中使用,因此pthreads v2的建议现已得到执行。
There have also been some changes to workers. Previously, there was a need to keep track of the work objects given to workers, otherwise if they were destroyed before having been executed by the worker thread, a segmentation fault would occur. This was well-known behavior and was demonstrated succinctly in the Multi-Threading in PHP with pthreads gist with the following snippet:
工人也发生了一些变化。 以前,需要跟踪提供给工人的工作对象,否则,如果在被工作线程执行之前将其销毁,则会发生分段错误。 这是众所周知的行为,并在PHP多线程中以pthreads gist和以下代码段简要地进行了演示:
class W extends Worker {
public function run(){}
}
class S extends Stackable {
public function run(){}
}
/* 1 */
$w = new W();
/* 2 */
$j = array(
new S(), new S(), new S()
);
/* 3 */
foreach ($j as $job)
$w->stack($job);
/* 4 */
$j = array();
$w->start();
$w->shutdown();
This is no longer an issue because the workers themselves now track the stacked work objects.
这不再是问题,因为工人自己现在可以跟踪堆积的工作对象。
Furthermore, there have been some changes around the meaning of method modifiers in pthreads v3. In pthreads v2, method modifiers had a special meaning in the context of Threaded objects. Specifically, protected methods had implicit synchronized access (enabling for them to be safely executed by multiple contexts), and private methods could only be executed by the context they were tied to. These differing semantics have now been removed due to reliability concerns.
此外,pthreads v3中的方法修饰符的含义已有一些更改。 在pthreads v2中,方法修饰符在Threaded对象的上下文中具有特殊含义。 具体来说,受保护的方法具有隐式同步访问(使它们可以被多个上下文安全地执行),而私有方法只能由它们所绑定的上下文来执行。 由于可靠性方面的考虑,这些不同的语义现已被删除。
For example, take the following snippet:
例如,使用以下代码段:
class ExampleThread extends Thread {
public $value = 0;
public function run()
{
$this->exclusive();
}
protected function exclusive()
{
for ($i = 0; $i < 10000; ++$i) {
++$this->value;
}
}
}
class Test extends ExampleThread {
public function callExclusive()
{
$this->exclusive();
}
};
$thread = new Test();
$thread->start();
$thread->callExclusive();
$thread->join();
var_dump($thread->value);
In pthreads v2, calling the ExampleThread::exclusive
method from both the main context and the new thread context was safe. The value output at the end of the script would always be int(20000)
. But in pthreads v3, this value can be anything from 1 to 20000 due to race conditions between the two unsynchronized for
loops.
在pthreads v2中,从主上下文和新线程上下文中调用ExampleThread::exclusive
方法是安全的。 脚本末尾的输出值始终为int(20000)
。 但是在pthreads v3中,由于两个未同步的for
循环之间的竞争条件,该值可以是1到20000之间的任何值。
In order to achieve the exact same behavior in pthreads v3, we must explicitly synchronize access using the built-in Threaded::synchronized
method. This need only be applied to the body of the ExampleThread::exclusive
method:
为了在pthreads v3中实现完全相同的行为,我们必须使用内置的Threaded::synchronized
方法显式同步访问。 这仅适用于ExampleThread::exclusive
方法的主体:
protected function exclusive()
{
$this->synchronized(function () {
for ($i = 0; $i < 10000; ++$i) {
++$this->value;
}
});
}
With respect to removing the private method modifier semantics, this has only lifted a previous restriction. Thus, code that utilized that behavior should not need any changing.
关于删除私有方法修饰符的语义,这仅解除了先前的限制。 因此,利用该行为的代码无需进行任何更改。
删除的课程 (Removed Classes)
The Mutex
and Cond
classes have been removed. This is because their functionality was not needed due to the synchronization features already provided by the Threaded
class. Using mutual exclusion locks and conditions in PHP code was never particularly safe either, since deadlocks could easily occur from erroneous code.
Mutex
和Cond
类已被删除。 这是因为由于Threaded
类已提供的同步功能,因此不需要它们的功能。 在PHP代码中使用互斥锁和条件也不是特别安全,因为死锁很容易因错误代码而发生。
The Collectable
class that extended Threaded
has also been removed. Now, we have a Collectable
interface instead which is implemented by Threaded
. The interface only enforces an isGarbage
method. The setGarbage
method is no longer needed because pthreads automatically handles when a task should be considered garbage (when the task has finished executing). The Threaded
class implements a default Threaded::isGarbage
method that should be used in the vast majority of cases. The default implementation will alway returns true
, since any task in the task queue is garbage (the task cannot be collected before being executed). Only in rare cases should a custom implementation be needed, and so overriding the Threaded::isGarbage
method should be a rarity.
扩展了Threaded
的Collectable
类也已删除。 现在,我们有了一个Collectable
接口,该接口由Threaded
实现。 该接口仅强制执行isGarbage
方法。 不再需要setGarbage
方法,因为pthreads自动处理应将任务视为垃圾的时间(当任务完成执行时)。 Threaded
类实现默认的Threaded::isGarbage
方法,该方法应在绝大多数情况下使用。 默认实现始终返回true
,因为任务队列中的任何任务都是垃圾(该任务在执行之前无法收集)。 仅在极少数情况下才需要自定义实现,因此重写Threaded::isGarbage
方法应该很少。
The following is a brief example of utilizing the built-in garbage collector in pthreads:
以下是在pthreads中利用内置垃圾收集器的简要示例:
$worker = new Worker();
for ($i = 0; $i < 10; ++$i) {
$worker->stack(new class extends Threaded {});
}
$worker->start();
while ($worker->collect()); // blocks until all tasks have finished executing and have been collected
$worker->shutdown();
Finally, the Stackable
class that was previously aliased to the Threaded
class has been removed. Any classes that extended Stackable
should now be changed to extend Threaded
.
最后,以前被别名为Threaded
类的Stackable
类已被删除。 现在,将扩展Stackable
所有类更改为扩展Threaded
。
删除的方法 (Removed Methods)
The following methods have been removed:
已删除以下方法:
Threaded::getTerminatedInfo
– due to it being unsafe to serialize exceptions. There are no built-in alternatives, but since PHP 7 has converted the vast majority of fatal errors to exceptions, catch-all exceptions handlers can be used instead:Threaded::getTerminatedInfo
–由于序列化异常是不安全的。 没有内置的替代方法,但是由于PHP 7将绝大多数致命错误转换为异常,因此可以使用包罗万象的异常处理程序:$task = new class extends Thread { public function run() { try { $this->task(); } catch (Throwable $ex) { // handle error here var_dump($ex->getMessage()); } } private function task() { $this->data = new Threaded(); $this->data = new StdClass(); // RuntimeException thrown var_dump(1); // never reached } }; $task->start() && $task->join();
(See below for the new
Volatile
class addition and subsequently why the above code is erroneous.)(有关新增的
Volatile
类的信息,请参见下面的内容,以及随后导致上述代码错误的原因。)Threaded::from
– since PHP 7 has anonymous classes, which are far more preferable to use.Threaded::from
–由于PHP 7具有匿名类,因此更可取。Threaded::isWaiting
– due to it simply not being needed when synchronizing. A thread should not have to question whether it is waiting for something, and as such, there are no alternatives to this method.Threaded::isWaiting
–由于在同步时根本不需要它。 线程不必询问它是否正在等待某些东西,因此,此方法没有替代方法。Threaded::lock
and its counterpartThreaded::unlock
– for the same reasons theMutex
andCond
classes were removed. Given that synchronization now syncs the properties table ofThreaded
objects, that should be used instead.Threaded::lock
及其对应的Threaded::unlock
–出于相同的原因,已删除Mutex
和Cond
类。 鉴于同步现在可以同步Threaded
对象的属性表,因此应该改用它。Thread::kill
– due to it not being safe to perform. There are no alternatives – code should simply not need to kill a thread in such a high-level environment.Thread::kill
–由于执行不安全。 没有其他选择–在如此高级的环境中,代码根本不需要杀死线程。Thread::detach
– due to it not being safe. There are no alternatives – any code relying on this will need to be rewritten.Thread::detach
–由于它不安全。 没有其他选择–依赖于此的任何代码都需要重写。Worker::isWorking
– due to it not being necessary. In order to see if a worker has any tasks left, theWorker::getStacked
method should be used, which will return the size of the remaining stack.Worker::isWorking
–由于没有必要。 为了查看工作程序是否还有任务,应该使用Worker::getStacked
方法,该方法将返回剩余堆栈的大小。
改变方法 (Changed Methods)
The following methods have been changed:
下列方法已更改:
Worker::unstack
– it no longer accepts a parameter (which previously removed the passed task from the stack). This means that the default now simply removes just the first task (the oldest one) from stack, rather than removing all tasks from the stack.Worker::unstack
–不再接受参数(先前已从堆栈中删除了传递的任务)。 这意味着默认值现在仅从堆栈中仅删除第一个任务(最旧的任务),而不是从堆栈中删除所有任务。Pool::collect
– it now returns the number of tasks to be collected, and the collector callback is now optional. If a collector callback is not used, the defaultWorker::collector
method is used.Pool::collect
–现在返回要收集的任务数,并且collector回调现在是可选的。 如果未使用收集器回调,则使用默认的Worker::collector
方法。
新班 (New Classes)
The Volatile
class has been added due to the new immutability semantics of Threaded
classes, where if they have properties that are Threaded
objects, then they are immutable. The Volatile
class enables for code that previously depended on the mutability of such members to be mutable once again.
由于Threaded
类具有新的不变性语义,因此添加了Volatile
类,在这种情况下,如果它们具有Threaded
对象的属性,则它们是不可变的。 Volatile
类使以前依赖于此类成员的可变性的代码再次可变。
For example, the following code snippet would have worked on pthreads v2:
例如,以下代码片段将在pthreads v2上运行:
class Task extends Threaded
{
public function __construct()
{
$this->data = new Threaded();
$this->data = new StdClass(); // previously ok, but not in pthreads v3
}
}
new Task();
But now in pthreads v3, the reassignment of $this->data
will throw a RuntimeException
due to it being a Threaded
property from a Threaded
class. In order to validly reassign the property, the Task
class should extend Volatile
instead:
但是现在在pthreads v3中, $this->data
的重新分配将引发RuntimeException
因为它是Threaded
类中的Threaded
属性。 为了有效地重新分配属性, Task
类应该扩展Volatile
:
class Task extends Volatile
{
public function __construct()
{
$this->data = new Threaded();
$this->data = new StdClass();
}
}
new Task();
Arrays being assigned to properties of Threaded
objects are now automatically coerced to Volatile
objects instead of Threaded
objects so that their behavior remains largely unchanged.
现在,分配给Threaded
对象属性的数组将自动强制为Volatile
对象而不是Threaded
对象,因此它们的行为在很大程度上保持不变。
Whilst this new immutability constraint increases complexity a little, it was introduced for the significant performance gains it gives to accessing Threaded
properties of Threaded
objects.
尽管此新的不变性约束稍微增加了复杂性,但引入它是为了显着提高访问Threaded
对象的Threaded
属性所获得的性能。
新方法 (New Methods)
The following methods have been added:
添加了以下方法:
Worker::collect
– this was introduced to enable for tasks that have finished executing on a worker’s stack to be freed. An optional collector function may be passed in, however the default collector (fromWorker::collector
) should be sufficient in the vast majority of cases.Worker::collect
–引入此功能是为了使在工作者堆栈上完成执行的任务能够被释放。 可以传入一个可选的收集器函数,但是在大多数情况下,默认的收集器(来自Worker::collector
)就足够了。For example, the following:
例如,以下内容:
$worker = new Worker(); var_dump(memory_get_usage()); // original memory usage for ($i = 0; $i < 500; ++$i) { $worker->stack(new class extends Threaded {}); } var_dump(memory_get_usage()); // memory usage after stacking 500 tasks $worker->start(); while ($worker->collect()); $worker->shutdown(); var_dump(memory_get_usage()); // memory usage after worker shutdown
Outputs:
输出:
int(372912) int(486304) int(374528)
With the line that invokes
Worker::collect
, the memory usage nearly returns back to normal. Without it, the memory usage would not have changed between the stacked 500 tasks and the shutting down of the worker. While the memory would have eventually been freed upon destroying the object, it is better to explicitly free this memory (particularly for long running processes that may need to execute many tasks). So always collect the garbage left by workers (as well as pools).在调用
Worker::collect
的行中,内存使用率几乎恢复正常。 没有它,在堆叠的500个任务与关闭工作器之间的内存使用情况将不会发生变化。 尽管销毁对象最终将释放内存,但是最好显式释放该内存(特别是对于长时间运行的进程,可能需要执行许多任务)。 因此,请务必收集工人(以及水池)留下的垃圾。Worker::collector
– this was introduced as the default implementation used by theWorker::collect
method. We can override this method for when we would like to delay the collecting of spent objects. As mentioned above, the default collector will be sufficient in the vast majority of cases, so only override this method if you know what you’re doing!Worker::collector
–这是Worker::collect
方法使用的默认实现。 当我们想要延迟收集用完的对象时,可以重写此方法。 如上所述,在大多数情况下,默认收集器就足够了,因此只有在知道自己在做什么的情况下才覆盖此方法!Threaded::notifyOne
– this complimentsThreaded::notify
by enabling for a signal to be sent only to one of the waiting synchronized contexts.Threaded::notifyOne
–通过启用仅将信号发送到等待的同步上下文之一来补充Threaded::notify
。
结论 (Conclusion)
There have been a number of changes to pthreads v3, making the extension both more performant and more robust. Some things have become simpler, particularly around shared resources that can only be handled via the synchronization mechanisms (Threaded::wait
and Threaded::notify
). Other things have increased a little in complexity, particularly with respect to the new immutability restrictions (in exchange for much better performance). But overall, pthreads v3 has received a nice cleanup and is looking ever better.
pthreads v3进行了许多更改,使扩展性能更高且更可靠。 有些事情变得更简单了,尤其是在只能通过同步机制( Threaded::wait
和Threaded::notify
)处理的共享资源周围。 其他方面的复杂性有所增加,特别是在新的不变性限制方面(以换取更好的性能)。 但总体而言,pthreads v3已得到很好的清理,并且看起来越来越好。
Are you using it? Did you have to update from v2 to v3? Tell us about it – we’d love to write about a hands-on upgrading example.
你在用吗? 您是否必须从v2更新到v3? 告诉我们-我们很乐意写一个动手升级的例子。
翻译自: https://www.sitepoint/upgrading-pthreads-v2-v3-look/
pthreads
更多推荐
pthreads_从Pthreads v2升级到v3:要注意的事项
发布评论