27

阅读一些关于锁定 PHP 的文章。
它们都主要指向http://php.net/manual/en/function.flock.php

本页讨论在硬盘上打开文件!!

真的是这样吗?我的意思是,这使得锁定非常昂贵 - 这意味着每次我想锁定我都必须访问硬盘)=

可以再用一个令人愉快的消息来安慰我吗?

编辑:

由于我在这里得到了一些回复,我想问这个;
我的脚本只能由一个线程运行,还是由多个线程运行?因为如果它是一个,那么我显然不需要互斥锁。有简洁的答案吗?

我到底想做什么

由 ircmaxell 询问。
这是故事:

我有两个 ftp 服务器。我希望能够在我的网站上显示有多少在线用户在线。
所以,我认为这些 ftp 服务器会将它们的统计信息“发布”到某个 PHP 脚本页面。假设这个页面的 URL 是“ http://mydomain.com/update.php ”。

在网站的主页(“ http://mydomain.com/index.php ”)上,我将显示累积统计信息(在线用户)。

就是这样。

我的问题是我不确定,当一个 ftp 服务器更新他的统计数据而另一个也这样做时,信息是否会混淆。
就像多线程时一样;两个线程同时增加一些“int”变量。除非您在它们之间同步,否则它不会按预期发生。
那么,我会有问题吗?是的,不,也许?

可能的解决方案

整天苦苦思索,我在这里有个想法,希望你给出你的意见。
如前所述,这些 ftp 服务器将每 60 秒发布一次他们的统计信息。
我正在考虑拥有这个文件“stats.php”。
它将包含在 ftp 服务器转到的更新脚本(“update.php”)和访问者看到有多少用户在线的“index.php”页面中。
现在,当 ftp 服务器更新时,“update.php”处的脚本将使用新的累积统计信息修改“stats.php”。
首先它将读取“stats.php”中包含的统计信息,然后累积,然后重写该文件。

如果我没记错的话,PHP 会检测到文件(“stats.php”)已更改并加载新文件。正确的?

4

5 回答 5

33

好吧,大多数 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用户级锁定 GET_LOCK('name', 'timeout'),或者使用 APC 或 XCache 之类的东西创建自己的锁定(注意,这不是真正的锁定,因为可能会在其他人在您的检查之间获得锁定的情况下创建竞争条件并接受锁)。

编辑:要回答您编辑的问题:

这完全取决于您的服务器配置。PHP 可以多线程运行(每个请求由不同的线程处理),也可以多进程运行(每个请求由不同的进程处理)。这完全取决于您的服务器配置...

PHP 很少会串行处理所有请求,只有一个进程(和一个线程)服务所有请求。如果您使用的是 CGI,那么默认情况下它是多进程的。如果您使用的是 FastCGI,则可能是多进程和多线程。如果您将 mod_php 与 Apache 一起使用,则取决于工作人员类型:

  1. mpm_worker 将是多进程和多线程的,进程数由 ServerLimit 变量决定。
  2. prefork 将是多进程的
  3. perchild 也将是多进程的

编辑:回答您的第二个编辑问题:

这很容易。将其存储在一个文件中:

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;
}
于 2010-05-27T13:39:33.363 回答
1

问题是:您将把 FTP 服务器通过 POST 推送到您的 update.php 文件的统计信息存储在哪里?如果它是本地文件,那么第二篇文章中的 ircmaxell 已经回答了您。您也可以使用互斥锁来执行此操作 - 信号量函数。另一种解决方案是使用 MySQL MyISAM 表来存储统计信息并使用类似update info_table set value = value + 1. 它应该锁定表,并序列化你的请求,你不会有任何问题。

于 2011-02-23T13:03:28.203 回答
1

我最近使用 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;
}
于 2019-03-28T13:47:09.727 回答
0

是的,这是真的,因为 PHP 是由 Apache 运行的,而 Apache 可以按照它认为最好的方式组织执行线程(请参阅各种工作模型)。因此,如果您想一次访问一个资源,您可以锁定到一个文件(例如,如果您正在处理 cron 作业,这很好),或者您依赖于数据库事务机制、ACID 特性和数据库资源锁定,如果您正在处理数据。

于 2010-05-27T13:39:14.880 回答
-4

PHP 不支持多线程,每个请求(因此每个 PHP 脚本)将仅在一个线程(甚至进程,取决于您运行 PHP 的方式)中执行。

于 2010-05-27T13:37:24.913 回答