2014-01-16 41 views
4

我已經搜索EVERYWHERE爲此,我有很多問題,但我不認爲這是與實際代碼的問題。 基本上這段代碼在兩個獨立的線程中啓動套接字服務器(登錄和遊戲),我基本上從非線程版本轉換了這段代碼,但我一直無法得到線程的這個工作。PHP Threading SimpleXMLElement

include "socket.php"; 
include "dep.php"; 

class Server extends Thread { 
    public $server; 
    public $config; 
    public function __construct($type){ 
     //$this->config = (string)$type; 
     $this->run2($type); 
     $this->run(); 
    } 
    public function run(){ 
     while(true){ 
      $this->server->loop(); 
     } 
    } 
    public function run2($config){ 
     $this->server = new sokserv($config); 
     $this->server->init(); 
     //while(true){ 
     // $this->server->loop(); 
     //} 
    } 
} 
$login = new Server('Config/config.xml'); 
$game = new Server("Config/config2.xml"); 
The error received is 
Fatal error: Uncaught exception 'Exception' with message 'Serialization of 'SimpleXMLElement' is not allowed' in C:\Users\=\Desktop\Test\Start.php:19 
Stack trace: 
#0 C:\Users\=\Desktop\Test\Start.php(19): Server->run2() 
#1 C:\Users\=\Desktop\Test\Start.php(10): Server->run2('Config/config.x...') 
#2 C:\Users\=\Desktop\Test\Start.php(26): Server->__construct('Config/config.x...') 
#3 {main} 
    thrown in C:\Users\=\Desktop\Test\Start.php on line 19 

但是,恢復到舊的代碼工作正常。 sokserv的第一位(我刪除了一些公共瓦爾,因爲它們包含的個人信息)

class sokserv 
{ 
    public $ip; 
    public $port; 
    public $users = array(); 
    public $config; 
    public $mysql; 
    private $socket; 
    public function __construct($config = "Config/config.xml") 
    { 
     $this->readConfig($config); 
    } 
    public function readConfig($file) 
    { 
     if (!file_exists($file)) 
      die("Could not find config"); 
     $this->config = simplexml_load_file($file); 
    } 

如果你想這是XML:

<server> 
    <port>6112</port> 
    <ip>0</ip> 
    <mysql> 
     <host>127.0.0.1</host> 
     <username>root</username> 
     <dbname></dbname> 
     <password></password> 
     <table></table> 
    </mysql> 
</server> 
+0

請分享sockserv的實現或源代碼鏈接。我想你可能需要將config var轉換爲數組。 –

+0

我無法分享sokserv課程,因爲它太大並且包含個人信息,但我可以分享涉及配置的前幾個位。 – user2601312

+0

是的,請您這樣做,你可能只是一個構造函數 –

回答

-1

因爲PHP本身並不支持之間共享數據線程中,pthreads代碼是序列化需要在兩個線程之間共享的數據,因此它可以在需要時(通過反序列化)在另一個線程中創建乾淨的副本。例外情況是簡單的標量類型,它可以很容易地複製,而Thread,WorkerStackable對象表示正在執行的實際任務,並且對同步有明確的支持。

不幸的是,一些PHP對象不支持序列化,因爲它們在內部實現爲指向內存中結構的指針,而不僅僅是一組PHP變量。 SimpleXML就是這樣的一個例子,因此你看到了錯誤信息。

大約有這幾個方面,取決於你如何「聰明」的希望:

  • 最簡單的就是不解析XML在sokserv->readConfig方法,要運行該方法立竿見影。相反,您可以存儲文件名或原始XML字符串,並在稍後進行解析。然後,每個線程將顯式創建自己的SimpleXML對象,並且只有字符串需要在線程之間傳遞。
  • 相反,您可以完全解析XML,將所需信息提取到一個更簡單的對象中,然後放棄SimpleXMLElement,所有這些都在啓動線程之前完成。
  • 另一種可能性是給你的sokserv__sleep and __wakeUp methods,或使其實現the Serializable interface。在這兩種情況下,這個想法都是將$this->config變成串行化/睡眠的XML字符串,然後在非序列化/喚醒的情況下回到(新的)SimpleXML對象中。
  • 最先進的方法是將SimpleXMLElement本身自定義序列化行爲。請注意,簡單的SimpleXMLElement子類無法重新實現Serializable接口,如this example所示。您可能需要將SimpleXMLElement放入某個容器對象中或使用委派模式。

無論選擇什麼途徑,基本概念是相同的:被傳遞線(連接到ThreadWorker,或Stackable對象)之間的數據不能是類如SimpleXML的不能被序列化的。

+0

@JoeWatkins我已經恢復編輯,因爲它基本上是一個完全不同的答案。請添加它,我會撤回我的或作爲一個更一般的解釋。 – IMSoP

+3

你的解釋是錯誤的,這就是我編輯它的原因。 –

+0

@JoeWatkins我會說這是「不完整」而不是「錯誤」 - 當然,我不知道pthreads有一個內置的解決方案(我甚至不知道OP是使用pthreads),但我正確地將問題診斷爲對象序列化以在上下文之間傳遞。包裝在由pthreads提供的特殊容器中的解決方案與用於實現'Serializable'的容器的包裝解決方案並不完全不同,而且您的附加信息證實,即使這些解決方案並非完全最佳,此處的解決方案也可以正常工作。 – IMSoP

8

Zend內存管理器在TSRM的幫助下有意地構建,以禁止上下文共享數據;沒有上下文可以分配或釋放另一個線程中的任何東西。這被稱爲無共享體系結構,它完全按照它所說的那樣進行。pthreads的工作是以安全,理智的方式突破這個障礙。

顯然,與PHP捆綁在一起的擴展沒有一個知道pthreads,我們也不希望它們是,因爲它們會變得非常複雜。當涉及到其他擴展提供的對象時,pthreads會假設最安全的事情是序列化對象以進行存儲。有些物體禁止這種情況發生,後代就是這樣一組物體。

從pthreads下降的字符串,浮點數,整數和對象不會被序列化。從pthreads下載的對象hi-jack序列化API,存儲對象的物理地址,通過直接訪問代表userland中對象的線程安全結構來避免序列化。

適當的解決方案是包裹要在一個目的是共享數據從並行線程下降:

<?php 
class Config extends Stackable { 
    /** 
    * Constructs thread safe, sharable configuration from complex XML 
    * @param mixed $xml   SimpleXMLElement or location of XML file 
    * @param array &$objects reference store 
    */ 
    public function __construct($xml, &$objects) { 
     if ($xml instanceof SimpleXMLElement) { 
      foreach ($xml as $key => $value) 
       $this[$key] = (string) $value; 
     } else { 
      foreach (simplexml_load_string(
         $xml) as $key => $value) { 
       if ($value->children()) { 
        $this[$key] = new Config($value, $objects); 
       } else $this[$key] = (string) $value; 
      } 
     } 

     /* maintain object references */ 
     $objects[] = $this; 
    } 

    public function run() {} 
} 

class Test extends Thread { 
    protected $config; 

    /** 
    * Constructs thread using pre-constructed thread safe, shared configuration object 
    * @param Config $config 
    */ 
    public function __construct(Config $config) { 
     $this->config = $config; 
    } 

    public function run() { 
     /* iterate over configuration */ 
     printf("%d settings:\n", count($this->config)); 
     foreach ($this->config as $key => $data) { 
      if (count($data) > 1) { 
       printf( 
        "\t%s, %d settings:\n", $key, count($data)); 
       foreach ($data as $name => $value) { 
        printf("\t\t%s = %s\n", $name, $value); 
       } 
      } else printf("\t%s = %s\n", $key, $data); 
     } 
     printf("\n"); 

     printf(
      "Host: %s:%d\n", 
      $this->config->ip, 
      $this->config->port); 

     printf(
      "MySQL: %[email protected]%s?database=%s&password=%s&table=%s\n", 
      $this->config->mysql->username, 
      $this->config->mysql->host, 
      $this->config->mysql->dbname, 
      $this->config->mysql->password, 
      $this->config->mysql->table); 
    } 
} 

/* Example XML */ 
$xml = <<<XML 
<server> 
    <port>6112</port> 
    <ip>some.ip</ip> 
    <mysql> 
     <host>127.0.0.1</host> 
     <username>root</username> 
     <dbname>somedb</dbname> 
     <password>somepass</password> 
     <table>sometable</table> 
    </mysql> 
</server> 
XML; 

/* Object reference storage */ 
$objects = []; 
$config = new Config($xml, $objects); 

$thread = new Test($config); 
$thread->start(); 

$thread->join(); 
?> 

將輸出下面:

3 settings: 
     port = 6112 
     ip = some.ip 
     mysql, 5 settings: 
       host = 127.0.0.1 
       username = root 
       dbname = somedb 
       password = somepass 
       table = sometable 

Host: some.ip:6112 
MySQL: [email protected]?database=somedb&password=somepass&table=sometable 

提供的示例使用[格式]您在問題中提供的XML,它採用該XML並創建它的線程安全表示形式,這將永遠不會被序列化。

Config的構造函數中的邏輯完全取決於您使用的XML的格式。

您可以將Config對象傳遞給任意多個線程,它們都可以讀/寫屬性並執行它的方法。

,你打算共享的所有數據都應以這種方式進行管理,要採取遠離這一切究竟是不是你應該變通異常,並嘗試存儲串行數據,而是你應該爲您的數據創建合適的容器,實際上支持正確的多線程

延伸閱讀:https://gist.github.com/krakjoe/6437782

+0

這個答案對我來說並不完全清楚:你說「一個pthreads的後代對象」,但是這個例子實際上是從一個'Stackable'類下降的,它顯然有一些'ArrayAccess'魔法,因爲你做了'$ this [ $關鍵]'。完整的文檔鏈接可能是好的。目前還不清楚這是否可以用來傳輸SimpleXML對象本身,因爲在這裏你只是從中提取字符串,這將不會與OP提供的XML一起工作,並且會在沒有任何特殊技巧的情況下序列化好。 – IMSoP

+0

它會序列化,但不是線程安全的,所以不共享。 http://php.net/intro。pthreads –

+0

所以,'Stackable'允許'Config'項目是可寫的,但實際上並沒有解決OP傳遞SimpleXML對象的問題? SimpleXML序列化問題的實際解決方法似乎是這樣一行:'$ this [$ key] =(string)$ value;' – IMSoP