pthreads

tech2022-07-04  172

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 counterpart Threaded::unlock – for the same reasons the Mutex and Cond classes were removed. Given that synchronization now syncs the properties table of Threaded 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, the Worker::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 default Worker::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 (from Worker::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 the Worker::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 compliments Threaded::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.com/upgrading-pthreads-v2-v3-look/

pthreads

相关资源:jdk-8u281-windows-x64.exe
最新回复(0)