php 7 中的同步块

Synchronized block in php 7

我来自 java 有同步块的背景:

The "Synchronized" keywords prevents concurrent access to a block of code or object by multiple Threads.

java中的示例代码:

public void addName(String name) {
    synchronized(this) {
        lastName = name;
        nameCount++;
    }
    nameList.add(name);
}

现在这个例子突出了 php 和 java 的根本区别(如果我错了请纠正我)。但是 php 中不存在单例或共享 class。因此,java 代码的给定示例在用作单例时很有意义。因此在请求之间有一个共享对象。遗憾的是,现在 php 似乎不存在,这显然是一个主要缺点。但是要以 php 方式进行,最终会将名称计数写入文件或数据库,从而以这种方式在请求之间共享数据(显然要慢很多)。但是问题是一样的:如果2个请求同时增加名称计数,那就太少了。

现在是第一个问题:php 7 是否存在类似的问题?即同步块

现在我不确定 php 7 中的线程这个词是否真的适用于我担心的事情。 php 中的线程是否也被认为是对 php 文件的单独调用让我们说 foo.php 也就是说,如果我同时访问 foo.php 两次,将同步块(如果存在)只一个接一个地执行,或者我是否必须通过扩展 class Thread 创建一个适当的 php 线程,然后才算作线程?

PHP 被设计成短命的 - PHP 应用程序诞生并随着 HTTP 请求而死。

每次您的网络服务器收到 HTTP 请求时,它都会调用您的应用程序的一个新实例。这个实例存在于它自己的进程中,有它自己的状态。

这意味着一个请求进程无法修改另一个请求进程的状态,但这也意味着您没有内置的方法来防止两个进程执行相同的代码。

这意味着您必须围绕此约束设计您的应用程序。

单身和共享 类 在 PHP 中确实存在

但是PHP是短暂的。对于每个 HTTP 请求,都会实例化一个新进程,并执行您的脚本。这意味着 synchronized 关键字对您的情况没有帮助。

应用程序设计避免了多个请求之间的竞争条件。

例如,在您的 SQL 查询中,您会执行 count = count + 1 之类的操作,而不是先读取 count,然后将其加一,然后写入。这与您在 Java 中用于并发的一些机制并没有什么不同(synchronized 通常是 最差的 解决并发问题的方法)。

对于数据库,您可以锁定 tables,并使用事务来确保多个查询的完整性。

I give you this example: paypal sometimes sends after each payment a confirmation TWICE at the exact same time. Now i do basically if($confirmed && doesNotExistYet()){ $c = new Customer(); $c->save();}

对您的数据库使用 UNIQUE 约束。或者锁定table。要不然。您有多种选择 - 但 synchronized 关键字无法解决问题,因为它们是 不同的进程

关于单例的一句话: Java 确保一个单例只存在一次 每个进程 。 PHP 做同样的事情。如果你 运行 同一个 JAR 2x 你会得到两个 Java 进程,每个进程都有自己的单例实例。

对您的问题的简短回答是不,PHP 不存在这样的问题,因为正如您指出的那样,PHP 不是 运行 多线程进程。 PHP 7 在这方面与以前的 PHP 版本相同。

现在您将缺少多线程描述为主要缺点。情况不一定如此:这是一个主要的差异。是不是劣势是另一个问题。这在很大程度上取决于上下文。

您描述的问题是进程间共享对象。共享对象对于没有多线程的PHP来说是不合逻辑的,但是共享对象的主要目的是共享对象内的数据

如果我们谈论的是共享数据,您说得对,数据库或文件是执行此操作的常用方法,并且通常在性能方面就足够了,但如果您确实需要更高的性能,您可以真正通过使用 Memcache 之类的东西共享内存中的数据。在 PHP 中有处理内存缓存的成熟库。这将是正常的 PHP 解决方案。

现在我想在这里提出另外两件可能与此相关的事情。

首先,让我将 NodeJS 添加到等式中,因为它的处理方式又有所不同。在 NodeJS 中,系统也是单线程的,但与 PHP 为每个请求启动一个新进程不同,在 NodeJS 中,所有请求都被馈送到一个持续不断的 运行ning 线程中。这意味着在 NodeJS 中,即使它是单线程的,您也可以拥有在请求之间共享的全局数据(通常是数据库连接),因为它们都在同一个进程中 运行ning。

这里的重点是单线程并不是 PHP 不能在请求之间共享数据的原因;更重要的是,在 PHP 中,每个请求都在其自己的进程中与其他请求隔离开来。这远非劣势,实际上可能是优势——例如,PHP 崩溃不会破坏您的整个站点,而在多线程或共享线程环境中它可以这样做。事实上,这是 NodeJS 最大的弱点之一:一点点写得不好的代码很容易使服务器完全无响应。

我想提出的第二件事是 PHP 的实验性分支实际上允许您在多线程和共享线程环境中使用该语言。在这两种情况下,这些都是非常非常 实验性的,当然不应该用于生产。正如您在问题中指出的那样,该语言缺少在这些环境中使用所必需的关键功能。但事实证明是可以做到的。

这些实验都不会真正去任何地方,因为现有的 PHP 代码在编写时并没有考虑到这些类型的环境。您已经知道如果您在不保护您的共享数据的情况下编写多线程 Java 程序会发生什么,所以应该清楚如果 PHP 曾经认真地娱乐 运行ning具有共享数据的平台,那么任何想要将其与现有 PHP 代码一起使用的人都需要进行大量重写。这不仅会发生,所以可以肯定地说 PHP 将坚持使用隔离进程的当前格式。

其他人回答的话很多,但没有代码。你为什么不试试这个?

function synchronized($handler)
{
    $name = md5(json_encode(debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS,1)[0]));
    $filename = sys_get_temp_dir().'/'.$name.'.lock';
    $file = fopen($filename, 'w');
    if ($file === false) {
        return false;
    }
    $lock = flock($file, LOCK_EX);
    if (!$lock) {
        fclose($file);
        return false;
    }
    $result = $handler();
    flock($file, LOCK_UN);
    fclose($file);
    return $result;
}

function file_put_contents_atomic($filename, $string)
{
    return synchronized(function() use ($filename,$string) {
        $tempfile = $filename . '.temp';
        $result = file_put_contents($tempfile, $string);
        $result = $result && rename($tempfile, $filename);
        return $result;
    });
}

以上代码使用自定义 "synchronized" 函数执行原子 file_put_contents

来源:https://tqdev.com/2018-java-synchronized-block-in-php