摘要:面試中有一個問題沒有很好的回答出來,題目為并發個請求,只要其中一個請求有結果,就返回,并中斷其他兩個。后來說了還說了兩種實現,一個是用另一個是用實現并發。所以總結了這篇文章,來講講中的并發。生成的可以中斷函數,并用向發送消息。
面試中有一個問題沒有很好的回答出來,題目為:并發3個http請求,只要其中一個請求有結果,就返回,并中斷其他兩個。
當時考慮的內容有些偏離題目原意, 一直在考慮如何中斷http請求,大概是在 client->recv() 之前去判斷結果是否已經產生,所以回答的是用 socket 去發送一個 http 請求,把 socket 加入 libevent 循環監聽,在callback中判斷是否已經得到結果,如果已經得到結果,就直接 return。
后來自己越說越覺得不對,既然已經recv到結果,就不能算是中斷http請求。何況自己從來沒用過libevent。后來說了還說了兩種實現,一個是用 curl_multi_init, 另一個是用golang實現并發。
golang的版本當時忘了close的用法,結果并不太符合題意。
這題沒答上來,考官也沒為難我。但是心里一直在考慮,直到面試完走到樓下有點明白什么意思了,可能考的是并發,進程線程的應用。所以總結了這篇文章,來講講PHP中的并發。本文大約總結了PHP編程中的五種并發方式,最后的Golang的實現純屬無聊,可以無視。如果有空,會再補充一個libevent的版本。
curl_multi_init
文檔中說的是 Allows the processing of multiple cURL handles asynchronously. 確實是異步。這里需要理解的是select這個方法,文檔中是這么解釋的Blocks until there is activity on any of the curl_multi connections.。了解一下常見的異步模型就應該能理解,select, epoll,都很有名,這里引用一篇非常好的文章,有興趣看下解釋吧。
// build the individual requests as above, but do not execute them
$ch_1 = curl_init("http://www.baidu.com/");
$ch_2 = curl_init("http://www.baidu.com/");
curl_setopt($ch_1, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch_2, CURLOPT_RETURNTRANSFER, true);
// build the multi-curl handle, adding both $ch
$mh = curl_multi_init();
curl_multi_add_handle($mh, $ch_1);
curl_multi_add_handle($mh, $ch_2);
// execute all queries simultaneously, and continue when all are complete
$running = null;
do {
curl_multi_exec($mh, $running);
$ch = curl_multi_select($mh);
if($ch !== 0){
$info = curl_multi_info_read($mh); if($info){ var_dump($info); $response_1 = curl_multi_getcontent($info["handle"]); echo "$response_1 "; break; }
}
} while ($running > 0);
//close the handles
curl_multi_remove_handle($mh, $ch_1);
curl_multi_remove_handle($mh, $ch_2);
curl_multi_close($mh);
這里我設置的是,select得到結果,就退出循環,并且刪除 curl resource, 從而達到取消http請求的目的。
swoole_client
swoole_client提供了異步模式,我竟然把這個忘了。這里的sleep方法需要swoole版本大于等于1.7.21, 我還沒升到這個版本,所以直接exit也可以。
$client = new swoole_client(SWOOLE_SOCK_TCP, SWOOLE_SOCK_ASYNC);
//設置事件回調函數
$client->on("connect", function($cli) {
$req = "GET / HTTP/1.1 Host: www.baidu.com Connection: keep-alive Cache-Control: no-cache Pragma: no-cache "; for ($i=0; $i < 3; $i++) { $cli->send($req); }
});
$client->on("receive", function($cli, $data){
echo "Received: ".$data." "; exit(0); $cli->sleep(); // swoole >= 1.7.21
});
$client->on("error", function($cli){
echo "Connect failed ";
});
$client->on("close", function($cli){
echo "Connection close ";
});
//發起網絡連接
$client->connect("183.207.95.145", 80, 1);
process
哎,竟然忘了 swoole_process, 這里就不用 pcntl 模塊了。但是寫完發現,這其實也不算是中斷請求,而是哪個先到讀哪個,忽視后面的返回值。
$workers = [];
$worker_num = 3;//創建的進程數
$finished = false;
$lock = new swoole_lock(SWOOLE_MUTEX);
for($i=0;$i<$worker_num ; $i++){
$process = new swoole_process("process"); //$process->useQueue(); $pid = $process->start(); $workers[$pid] = $process;
}
foreach($workers as $pid => $process){
//子進程也會包含此事件 swoole_event_add($process->pipe, function ($pipe) use($process, $lock, &$finished) { $lock->lock(); if(!$finished){ $finished = true; $data = $process->read(); echo "RECV: " . $data.PHP_EOL; } $lock->unlock(); });
}
function process(swoole_process $process){
$response = "http response"; $process->write($response); echo $process->pid," ",$process->callback .PHP_EOL;
}
for($i = 0; $i < $worker_num; $i++) {
$ret = swoole_process::wait(); $pid = $ret["pid"]; echo "Worker Exit, PID=".$pid.PHP_EOL;
}
pthreads
編譯pthreads模塊時,提示php編譯時必須打開ZTS, 所以貌似必須 thread safe 版本才能使用. wamp中多php正好是TS的,直接下了個dll, 文檔中的說明復制到對應目錄,就在win下測試了。 還沒完全理解,查到文章說 php 的 pthreads 和 POSIX pthreads是完全不一樣的。代碼有些爛,還需要多看看文檔,體會一下。
class Foo extends Stackable {
public $url; public $response = null; public function __construct(){ $this->url = "http://www.baidu.com"; } public function run(){}
}
class Process extends Worker {
private $text = ""; public function __construct($text,$object){ $this->text = $text; $this->object = $object; } public function run(){ while (is_null($this->object->response)){ print " Thread {$this->text} is running "; $this->object->response = "http response"; sleep(1); } }
}
$foo = new Foo();
$a = new Process("A",$foo);
$a->start();
$b = new Process("B",$foo);
$b->start();
echo $foo->response;
yield
yield生成的generator,可以中斷函數,并用send向 generator 發送消息。稍后補充協程的版本。還在學習中。
Golang
用Go實現比較簡單, 回家后查了查 close,處理一下 panic就ok了。代碼如下:
package main
import (
"fmt"
)
func main() {
var result chan string = make(chan string, 1) for index := 0; index< 3; index++ { go doRequest(result) } res, ok := <-result if ok { fmt.Println("received ", res) }
}
func doRequest(result chan string) {
response := "http response" defer func() { if x := recover(); x != nil { fmt.Println("Unable to send: %v", x) } }() result <- response close(result)
}
上面的幾個方法,除了 curl_multi_* 貌似符合題意外(不確定,要看下源碼),其他的方法都沒有中斷請求后recv()的操作, 如果得到response后還有后續操作,那么是有用的,否則并沒有什么意義。想想可能是PHP操作粒度太大, 猜測用 C/C++ 應該能解決問題。
寫的時候沒有注意到一個問題,有些方式是返回值,有些直接打印了,這樣不好,應該統一使用返回值得到請求結果。能力有限,先這樣吧。
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://m.specialneedsforspecialkids.com/yun/22786.html
摘要:這里呢,我直接給出高并發場景通常都會考慮的一些解決思路和手段結尾如何有效的準備面試中并發類問題,我已經給出我的理解。 showImg(https://segmentfault.com/img/bV7Viy?w=550&h=405); 主題 又到面試季了,從群里,看到許多同學分享了自己的面試題目,我也抽空在網上搜索了一些許多公司使用的面試題,目前校招和社招的面試題基本都集中在幾個大方向上...
摘要:并發表示在一段時間內有多個動作存在。并發帶來的問題在享受并發編程帶來的高性能高吞吐量的同時,也會因為并發編程帶來一些意想不到弊端。并發過程中多線程之間的切換調度,上下文的保存恢復等都會帶來額外的線程切換開銷。 0x01 什么是并發 要理解并發首選我們來區分下并發和并行的概念。 并發:表示在一段時間內有多個動作存在。 并行:表示在同一時間點有多個動作同時存在。 例如:此刻我正在寫博客,但...
摘要:我思考的是什么才算是高并發你一天幾個肯定高不了。所以我得出一個自定義概念如果某個系統的日在千萬級別以上,他就可能是一個高并發的系統。高并發的問題,我們具體該關心什么講真話,高并發是個比較抽象的概念。是指秒鐘響應的請求數量。 這并不是一個回答的問題的文章,而是由此引發的一個思考。 大家心里仔細想想,當你們聽到高并發網站時,心里對這個網站是個什么概念?首先想到的是淘寶嗎?帶著問題,我們一起...
摘要:而爬蟲一般用多線程來控制并發,然而如果是爬蟲,由于其單線程無阻塞性質以及事件循環機制,一般不用多線程來控制并發當然也可以實現多線程,此處非重點不再多講,而是更加簡便地直接在代碼層級上實現并發。下面我們用行代碼實現一個并發控制的函數。 前言 首發于 github blog 做過爬蟲的都知道,要控制爬蟲的請求并發量,其實也就是控制其爬取頻率,以免被封IP,還有的就是以此來控制爬蟲應用運...
閱讀 1113·2021-09-22 15:37
閱讀 1138·2021-09-13 10:27
閱讀 2479·2021-08-25 09:38
閱讀 2453·2019-08-26 11:42
閱讀 1535·2019-08-26 11:39
閱讀 1563·2019-08-26 10:58
閱讀 2326·2019-08-26 10:56
閱讀 2575·2019-08-23 18:08