我来自java背景,其中有同步块:
“已同步”关键字阻止对块的并发访问 多个线程的代码或对象。
java中的示例代码:
public void addName(String name) {
synchronized(this) {
lastName = name;
nameCount++;
}
nameList.add(name);
}
现在这个例子突出了php和java的根本区别(如果我错了,请纠正我)。但是php中不存在单例或共享类。因此,当作为单例使用时,给出的java代码示例很有意义。因此在请求之间具有共享对象。遗憾的是,对于php来说,这似乎并不存在,这显然是一个主要的缺点。但要以php方式实现,最终会将名称计数写入文件或数据库,从而在请求之间共享数据(显然要慢得多)。但问题是一样的:如果2个请求在同一时间增加了名字数,那么它就会太少了。
现在第一个问题:为PHP 7做了类似的事情吗?也就是说,同步块
现在我不确定在php 7中,单词thread是否真的适用于我担心的内容。在PHP中的一个线程被认为是对一个php文件的单独调用,让我们说foo.php
,如果我同时访问foo.php
两次,那么同步块(如果存在的话)是只是一个接一个地执行,或者我是否必须通过扩展类Thread
来创建一个合适的php线程,然后才算作线程?
答案 0 :(得分:2)
对你的问题的简短回答是否,对PHP来说不存在这样的情况,因为正如你所指出的那样PHP不会运行多线程进程。 PHP 7在这方面与以前的PHP版本相同。
现在您将缺乏多线程描述为主要缺点。情况并非如此:它是一个主要的差异。它是否是一个劣势是另一个问题。这在很大程度上取决于背景。
您描述的问题是在进程之间拥有共享对象。对于没有多线程的PHP,共享对象是非顺从的,但共享对象的主要目的是在对象内共享数据。
如果我们正在谈论共享数据,那么你是正确的,数据库或文件是一种常见的方式,并且在性能方面通常已经足够了,但如果你真的需要更多的性能,可以通过使用像Memcache这样的东西真正地共享内存中的数据。在PHP中有完善的库来处理memcache。这将是正常的PHP解决方案。
现在我还想提出另外两件可能与此相关的事情。
首先,让我将NodeJS添加到等式中,因为这会使事情再次发生变化。在NodeJS中,系统也是单线程的,但与PHP不同,它为每个请求启动一个新进程,在NodeJS中,所有请求都被送入一个不断运行的线程。这意味着在NodeJS中,即使它是单线程的,您也可以拥有请求之间共享的全局数据(通常是数据库连接),因为它们都在同一个进程中运行。
这里的要点是,单线程并不是PHP无法在请求之间共享数据的原因。它更多的是在PHP中,每个请求在其自己的进程中与其他请求隔离。这实际上可能是一个优势 - 例如,PHP崩溃不会占用整个站点,而是可以在多线程或共享线程环境中执行。事实上,这是NodeJS最大的弱点之一:只需一点编写不好的代码就可以使服务器完全没有响应。
我想提出的第二件事是,实际上PHP的实验分支实际上允许您将语言用于多线程和共享线程环境。在这两种情况下,这些都非常非常实验,当然不应该用于生产。正如您在问题中所述,该语言缺少在这些环境中使用所必需的关键功能。但事实是它可以做到。
这些实验都不会真正去任何地方,因为现有的PHP代码并没有考虑到这些环境。您已经知道如果编写多线程Java程序而不保护共享数据会发生什么,所以应该很清楚,如果PHP曾经认真地在具有共享数据的平台上运行,那么任何想要使用它的人都应该使用它现有的PHP代码需要进行大量的重写。这不仅仅会发生,所以可以肯定地说,PHP将坚持使用它的当前格式和孤立的进程。
答案 1 :(得分:1)
PHP被设计为短暂的 - 一个PHP应用程序诞生并被HTTP请求杀死。
每当您的网络服务器收到HTTP请求时,它都会调用您的应用程序的新实例。这个实例以它自己的状态存在于它自己的过程中。
这意味着一个请求进程无法修改另一个请求进程的状态,但这也意味着您没有内置的方法来阻止两个进程执行相同的代码。
这意味着您必须围绕此约束设计应用程序。
答案 2 :(得分:1)
单例和共享类在PHP中存在。
但PHP是短暂的。对于每个HTTP请求,都会实例化一个新进程,并执行您的脚本。这意味着synchronized
kayword对您的情况没有帮助。
应用程序设计可以避免多个请求之间的竞争条件。
例如,在SQL查询中,您可以执行count = count + 1
之类的操作,而不是先读取count
,然后再将其增加1,然后再编写。这与您在Java中用于并发的一些机制没有什么不同(synchronized
通常是最差解决并发问题的方法)。
对于数据库,您可以锁定表,并使用事务来确保多个查询的完整性。
我举了这个例子:paypal有时会在每次付款后同时发送确认TWICE。现在我基本上做了($ confirm&& doesNotExistYet()){$ c = new Customer(); $ C->保存();}
在数据库上使用UNIQUE约束。或者锁定桌子。要不然。您有选项 - 但synchronized
关键字无法解决问题,因为这些不同的流程。
关于单身人士的一句话: Java确保一个单例仅存在一次每个进程。 PHP也是如此。如果运行相同的JAR 2x,则会获得两个Java进程,每个进程都有自己的单例实例。
答案 3 :(得分:1)
其他人用很多单词回答,但没有代码。你为什么不尝试这个?
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;
});
}
上面的代码使用自定义的“同步”功能执行原子file_put_contents
。