阅读一些关于在PHP中锁定的文章。 它们主要都指向http://php.net/manual/en/function.flock.php。
这个页面讨论了在硬盘上打开文件!!
真的是这样吗?我的意思是,这使得锁定非常昂贵 - 这意味着每次我想要锁定我都必须访问硬盘)=
可以用一个令人愉快的消息来安慰我吗?
编辑:
由于我有一些回复,我想问这个; 我的脚本只能运行一个或几个线程?因为如果它是一个然后我显然不需要互斥量。有简明的答案吗?
究竟我想要做什么
由ircmaxell提问。 这是故事:
我有两个ftp服务器。我希望能够在我的网站上显示有多少在线用户在线。 所以,我认为这些ftp服务器会将他们的统计信息“POST”到某个PHP脚本页面。我们假设这个页面的URL是“http://mydomain.com/update.php”。
在网站的主页(“http://mydomain.com/index.php”)上,我将显示累积统计信息(在线用户)。
而已。
我的问题是,我不确定当一个ftp服务器更新他的统计数据而另一个ftp服务器也是如此时,信息会变得混杂。 就像多线程时一样;两个线程同时增加一些“int”变量。除非您在它们之间进行同步,否则它不会按预期发生。 那么,我有问题吗?是的,不,也许吧?
可能解决方案
一整天都在努力思考,我在这里有一个想法,我想让你发表意见。 正如所说的这些ftp服务器将发布他们的统计数据,每60秒一次。 我正在考虑将此文件设为“stats.php”。 它将包含在ftp服务器转到的更新脚本(“update.php”)和“index.php”页面中,访问者可以看到有多少用户在线。 现在,当ftp服务器更新时,“update.php”中的脚本将使用新的累积统计信息修改“stats.php”。 首先,它将读取“stats.php”中包含的统计信息,然后累积,然后重写该文件。
如果我没弄错,PHP将检测到文件(“stats.php”)已更改并加载新文件。正确?
好吧,大多数PHP在不同的进程空间中运行(线程实现很少)。容易的就是羊群。它保证可以在所有平台上运行。
但是,如果您在支持中编译,则可以使用其他一些内容,例如Semaphore扩展。 (使用--enable-sysvsem编译PHP)。然后,你可以做类似的事情(注意,sem_acquire()应该阻止。但如果由于某种原因它不能,它将返回false):
$sem = sem_get(1234, 1);
if (sem_acquire($sem)) {
//successful lock, go ahead
sem_release($sem);
} else {
//Something went wrong...
}
您拥有的其他选项是MySQL user level locks GET_LOCK('name', 'timeout')
,或使用像APC或XCache这样的东西创建您自己的(注意,这不是真正的锁定,因为可能会创建竞争条件,其他人在您的支票和接受锁)。
编辑:回答您编辑过的问题:
这一切都取决于您的服务器配置。 PHP可以运行多线程(每个请求由不同的线程提供),也可以运行多进程(每个请求由不同的进程提供)。这一切都取决于您的服务器配置......
PHP非常罕见地将串行服务所有请求,只有一个进程(和一个线程)为所有请求提供服务。如果你正在使用CGI,那么它默认是多进程的。如果您使用的是FastCGI,则可能是多进程和多线程。如果你将mod_php与Apache一起使用,那么它取决于worker类型:
编辑:回答您的第二个编辑问题:
这很容易。将其存储在一个文件中:
function readStatus() {
$f = fopen('/path/to/myfile', 'r');
if (!$f) return false;
if (flock($f, LOCK_SH)) {
$ret = fread($f, 8192);
flock($f, LOCK_UN);
fclose($f);
return $ret;
}
fclose($f);
return false;
}
function updateStatus($new) {
$f = fopen('/path/to/myfile', 'w');
if (!$f) return false;
if (flock($f, LOCK_EX)) {
ftruncate($f, 0);
fwrite($f, $new);
flock($f, LOCK_UN);
fclose($f);
return true;
}
fclose($f);
return false;
}
function incrementStatus() {
$f = fopen('/path/to/myfile', 'rw');
if (!$f) return false;
if (flock($f, LOCK_EX)) {
$current = fread($f, 8192);
$current++;
ftruncate($f, 0);
fwrite($f, $current);
flock($f, LOCK_UN);
fclose($f);
return true;
}
fclose($f);
return false;
}
问题是:在哪里将FTP服务器推送的统计信息存储到您的update.php文件中?如果它是本地文件,则第二篇帖子中的ircmaxell已经回复了你。您也可以使用互斥锁执行此操作 - 信号量函数。另一种解决方案是使用MySQL MyISAM表来存储统计数据并使用像update info_table set value = value + 1
这样的东西。它应该锁定表,并序列化您的请求,您将没有任何问题。
是的,因为PHP由Apache运行,而Apache可以组织执行线程,因为它认为最好(参见各种工作者模型)。因此,如果您想一次访问一个资源,您可以锁定一个文件(例如,如果您正在处理cron作业,这是好的),或者您依赖于数据库事务机制,ACID功能和数据库资源锁定,如果你正在处理数据。
我最近使用PHP的flock函数创建了我自己的类似互斥体机制的简单实现。关闭源代码可以改进下面的代码,但它适用于大多数用例。
function mutex_lock($id, $wait=10)
{
$resource = fopen(storage_path("app/$id.lck"),"w");
$lock = false;
for($i = 0; $i < $wait && !($lock = flock($resource,LOCK_EX|LOCK_NB)); $i++)
{
sleep(1);
}
if(!$lock)
{
trigger_error("Not able to create a lock in $wait seconds");
}
return $resource;
}
function mutex_unlock($id, $resource)
{
$result = flock($resource,LOCK_UN);
fclose($resource);
@unlink(storage_path("app/$id.lck"));
return $result;
}
PHP不支持多线程,每个请求(因此每个PHP脚本)只能在一个线程中执行(甚至进程,具体取决于您运行PHP的方式)。