前置條件:
所有測試生成的都寫入乙個新檔案,如果是同乙個檔名,那麼每次執行指令碼前,需要把該日誌檔案刪掉,確保每次執行時日誌檔案都是重新建立的。
每次執行都是往日誌檔案中使用多程序寫入90000行日誌。每種方式分成四種對照組測試:
30*3000 加鎖(即30個程序每個程序寫入3000行,總共90000行,寫入時需對日誌檔案上獨佔鎖)。
30*3000 不加鎖(即30個程序每個程序寫入3000行,總共90000行,寫入時日誌檔案不上鎖)。
90*1000 加鎖(即90個程序每個程序寫入1000行,總共90000行,寫入時需對日誌檔案上獨佔鎖)。
90*1000 不加鎖(即90個程序每個程序寫入1000行,總共90000行,寫入時日誌檔案不上鎖)。
方式一:
加鎖:(n=3000 | n=1000)
for($i=0;$i$msg = "test text";
不加鎖:(n=3000 | n=1000)
for($i=0;$i$msg = "test text";
執**況如下表:
序號程序數
每個程序寫入行數
是否加鎖
第一次執行平均耗時(s)
第二次執行平均耗時(s)
第三次執行平均耗時(s)
1-1y
2.831
2.815
2.861
1-2n
2.826
2.855
2.751
1-3y
2.407
2.396
2.278
1-4n
1.779
2.052
2.01
方式二:
加鎖:(n=3000 | n=1000)
$handle = fopen($log,』a』);
flock($handle,lock_ex);
for($i=0;$i$msg = "test text";
fwrite($handle,$msg);
flock($handle,lock_un);
fclose($handle);
不加鎖:(n=3000 | n=1000)
$handle = fopen($log,』a』);
for($i=0;$i$msg = "test text";
fwrite($handle,$msg);
fclose($handle);
執**況如下表:
序號程序數
寫入行數/每個程序
是否加鎖
第一次執行平均耗時(s)
第二次執行平均耗時(s)
第三次執行平均耗時(s)
2-1y
0.66
0.659
0.658
2-2n
1.272
1.17
1.161
2-3y
0.83
0.855
0.836
2-4n
0.952
1.097
0.947
以方式一跟方式二的**為參照,同一種方式,上不上鎖,效能相差不是很大,從效率上講,方式二要比方式一高效。
最根本的原因是file_put_contents()函式每次執行相當於執行了 fopen(),fwrite(),fclose()三個函式,所以單次執行耗時會比較長。
如果把方式二做個調整,比如把fopen()和fclose都放進for迴圈裡,那麼方式二跟方式一基本沒太大差別。比如下面**:
for($i=0;$i$handle = fopen($log,』a』);
//flock($handle,'lock_ex');
$msg = "test text";
fwrite($handle,$msg);
//flock($handle,'lock_un');
fclose($handle);
當然,如果用這種寫法本身就不合理,還不如直接使用file_put_contents()來的簡單。
不上鎖的情況,日誌寫進去時無序的,各個程序之間穿插著寫入一行日誌。
上鎖的情況,日誌相對有序,基本是乙個程序寫完n行後釋放了獨佔鎖才輪到另乙個程序。但是程序之間也是無序的。比如第乙個子程序寫完,被第5個子程序搶到獨佔鎖,那麼就是第5個子程序先寫,第二個只能繼續等。所以,上鎖的情況同乙個程序寫的日誌才是有序的。
<?php
set_time_limit(30);
$log = '/data/tmp/a.log';
for($i = 0;$i<30;$i++)
flock($handle,lock_un);
fclose($handle);
unset($handle);
$end = microtime(true);
$s = round($end-$start,3);echo "程序:,開始:,結束:,耗時:".php_eol;
}finallyelse}}
}echo 'over'.php_eol;
系統層面上對每個寫入請求之前的位置更新操作應該具有原子性,且對每個寫操作也是具有完整性保證的。不會導致兩個寫操作交叉執行的情況。
那麼在上鎖的情況下,如果某個子程序在解除檔案鎖之前就掛掉了,會不會導致檔案被鎖死而導致其他程序一直等待呢?
這裡做個測試:開5個子程序,每個程序寫入5行日誌,日誌編號序號(子程序編號:日誌編號)總共25行日誌。
如果在第三個子程序上了獨佔鎖,然後寫入第三行日誌前,讓該子程序退出。具體過程如下:
<?php
set_time_limit(30);
$log = '/data/tmp/a.log';
for($i = 1;$i<=5;$i++)
$start_time = microtime(true);
//todo 其他業務邏輯
//打點記錄並行任務執行狀況
$fid = posix_getpid();
$ffid = posix_getppid();
$date = date('ymdhis');
$end_time = microtime(true);
$usetime = round($end_time-$start_time,2);
fwrite($handle,$msg);
}flock($handle,lock_un);
fclose($handle);
unset($handle);
$end = microtime(true);
$s = round($end-$start,3);
echo php_eol.$s.',';
//echo "程序:,開始:,結束:,耗時:".php_eol;
}finallyelse}}
}echo 'over'.php_eol;
最終得到得日誌總數時22行,因為第3個子程序只寫了2行就退出了,執行結果如下圖:
由圖可見,就算第三個子程序中途退出了,沒有釋放日誌檔案的獨佔鎖,但是其他程序仍然正常按照獨佔的方式寫入日誌。
原因是當子程序掛掉的時候,該子程序對日誌檔案的獨佔鎖也會被自動解除。所以就算某個子程序上完獨佔鎖,沒來得及解除就退出了,也不用擔心會影響到其他程序對該日誌檔案得使用。
另外,使用 pcntl_fork() 建立程序時需要注意的一些點
pcntl_fork()函式執行的時候,會建立乙個子程序。該子程序會複製當前程序,也就是父程序的所有的變數資料,**,還有狀態。也就是說,在乙個子程序建立之前,定義的變數,常量,函式等,在子程序內都可以使用。
如果建立成功,並且該子程序在父程序內,則返回0,在子程序內返回自身的程序號,失敗則返回-1。
(1)當我們在 for 迴圈 或者 foreach 的迴圈裡建立子程序,那麼在子程序執行的結尾記得將子程序殺死,不然子程序也會進入 for 迴圈和 foreach 迴圈,從而形成遞迴建立子程序的情況。
例如:$arr = array(1,2....n);
foreach($arr as $k=>$v) finally {} 中將子程序殺死,不讓其進入遞迴。
(2)不論時使用for迴圈還是foreach迴圈,都不會按照順序去執行。
比如第(1)部分的兩個例子中,可能最後乙個子程序先執行,最終先進入迴圈遞迴,結果第n個子程序執行了2n次。
而第乙個子程序程序如果最後執行到,就只能執行1次。當然這是在每個子程序執行完沒有殺死的情況。比如:
<?php
$pid = $fid = posix_getpid();
$arr = array('num1','num2','num3','num4');
foreach($arr as $k=>$v);主程序id:; 父程序id:; 當前程序id:;".php_eol;
echo $msg;
}}?>
結果: python寫入csv檔案的幾種方法總結
最常用的一種方法,利用pandas包 import pandas as pd 任意的多組列表 a 1,2,3 b 4,5,6 字典中的key值即為csv中列名 dataframe pd.dataframe 將dataframe儲存為csv,index表示是否顯示行名,default true dat...
python寫入csv檔案的幾種方法總結
最常用的一種方法,利用pandas包 import pandas as pd 任意的多組列表 a 1,2,3 b 4,5,6 字典中的key值即為csv中列名 dataframe pd.dataframe 將dataframe儲存為csv,index表示是否顯示行名,default true dat...
php 讀取檔案的幾種方法
檔案操作的三個步驟,開啟,操作,關閉。fopen fopen 路徑,方式 fwrite fopen,寫入的字串 fclose fopen 其中開啟方式有如下幾種方式 模式描述 r唯讀。在檔案的開頭開始。r 讀 寫。在檔案的開頭開始。w只寫。開啟並清空檔案的內容 如果檔案不存在,則建立新檔案。w 讀 ...