6

我目前正在使用以下散列方法resource进行查找:

$foo = socket_create(...);
$bar = socket_create(...);

$map[(int)$foo] = 'foo';
$map[(int)$bar] = 'bar';

echo $map[(int)$foo]; // "foo"

integer铸造是最好的选择吗?如果不是,还有什么其他散列方法会更好或更有效?这些查找将在一个紧密循环(套接字轮询)中以每秒数百次的方式在一个集合中执行,因此我已经排除了基于迭代的解决方案。

编辑:

为了更好地解释我的情况,该socket_select()函数通过引用获取套接字资源数组并对其进行修改,以便在函数调用之后,它们将仅包含已更改的资源(例如,准备好从中读取)。我使用一个Socket类作为套接字资源的包装器,以使我的代码更加抽象和可测试:

$socketObject = new Socket($socketResource);

我的另一个类保留了每次调用时都需要轮询的所有套接字资源的列表socket_select()

$reads = [$socketResource1, $socketResource2, ...];
socket_select($reads, null, null, 0);

在调用 之后socket_select(),我知道哪些套接字资源发生了变化,但是要在我的代码中做任何有意义的事情,我需要知道这些资源对应于哪些套接字对象。因此,我需要一些方法将套接字资源映射到它们的对象:

foreach ($reads as $socketResource) {
    // Which socket object does $socketResource correspond to here?
    // Currently, I use a solution like this:
    $socketObject = $this->map[(int)$socketResource];
    // Unfortunately, this behavior isn't guaranteed, so it isn't reliable...
}
4

4 回答 4

7

将资源转换为整数时观察到的行为未定义(请参阅页面底部的注意事项)。因此,即使它现在有效并且可靠地运行了很长时间,您也必须意识到,没有任何事情可以依赖而不通知您进行更改。

澄清后编辑:

不要将资源作为键,而是使用两个数组。一种将 Socket 对象的哈希映射到实际对象。另一个将相同的哈希映射到资源。然后将后一个数组传递给socket_select. 在函数不会改变数组键的前提下,然后可以迭代数组并使用键在O(1)中查找Socket:

$r1 = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
$r2 = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);

$s1 = new Socket($foo);
$s2 = new Socket($bar);

$socketMap = array(
    spl_object_hash($s1) => $s1,
    spl_object_hash($s2) => $s2
);

$reads = array(
    spl_object_hash($s1) => $r1,
    spl_object_hash($s2) => $r2
);

socket_select($reads, null, null, 0);

foreach (array_keys($reads) as $hash) {
    $socketObject = $socketMap[$hash];
}

更新:将资源转换为整数不再未定义,如链接的手册页中所示。如果将资源转换为整数,则结果将是 PHP 在运行时分配给资源的唯一资源编号。

于 2013-07-29T16:27:04.063 回答
2

我将此函数与 multi_curl 一起使用,对于排序以确保文本不会随机排列非常有效:

function get_resource_id($resource) {
    if (!is_resource($resource))
        return false;

    return array_pop(explode('#', (string)$resource));
}
于 2013-07-21T19:51:18.113 回答
0

我建议您创建一个集合对象而不是$map变量,例如。

class ResourceCollection implements ArrayAccess {

  private $map = array();

  /**
   * returns index of element or FALSE if not existent
   */
  protected function getIndex($offset){
    if(is_resource($offset)){
      $index = array_search($offset, $this->map);
    }
    else // you can add more tests if you need
      if(isset($this->map[$offset]))
        $index = $offset;
      else
        $index = false;
    return $index;
  }

  /**
   * required by ArrayAccess interface
   */
  public function offsetExists($offset){
    return ($this->getIndex($offset) === false)? false : true;
  }



  /**
   * required by ArrayAccess interface
   */
  public function offsetGet($offset){
    $index = $this->getIndex($offset);
    if($index === false)
      throw new ... // or handle error of non-existent element
    return $this->map[$index];
  }

// etc., implement ArrayAccess interface, Iterator and anything you want
}

虽然我没有测试过,但这应该允许您访问对象,就好像它是一个数组一样,我希望这种方式(没有关于此的文档)资源可以用作数组索引。

于 2013-07-31T08:56:12.397 回答
0

我知道这个问题很老了,但是从今天开始,您还可以使用linked-hash-map并将资源用作数组键。

您可以使用Composer安装它:

composer require tonix-tuft/linked-hash-map

并像这样使用它:

<?php

// ...

use LinkedHashMap\LinkedHashMap;

$map = new LinkedHashMap();

$foo = socket_create(...);
$bar = socket_create(...);

$map[$foo] = 'foo';
$map[$bar] = 'bar';

echo $map[$foo]; // "foo"
于 2021-07-17T09:07:55.987 回答