在python 進程、線程 (一)中簡單的說過,CPython中的GIL使得同一時刻只能有一個線程運行,即并發執行。并且即使是多核CPU,GIL使得同一個進程中的多個線程也無法映射到多個CPU上運行,這么做最初是為了安全著想,慢慢的也成為了限制CPython性能的問題。
就像是一個線程想要執行,就必須得到GIL,否則就不能拿到CPU資源。但是也不是說一個線程在拿到CPU資源后就一勞永逸,在執行的過程中GIL可能會釋放并被其他線程獲取,所以說其它的線程會與本線程競爭CPU資源。
在understand GIL:http://www.dabeaz.com/python/UnderstandingGIL.pdf中有關于GIL釋放和GIL的概要。
多線程在python2中:當一個線程進行I/O的時候會釋放鎖,另外當ticks計數達到100(ticks可以看作是Python自身的一個計數器,也可對比著字節碼指令理解,專門做用于GIL,每次釋放后歸零,這個計數可以通過 sys.setcheckinterval 來調整)。鎖釋放之后,就涉及到線程的調度,線程的鎖進行,線程的切換。這是會消耗CPU資源,因此會造成程序性能問題和等待時延。特別是在CPU密集型代碼時。
但是對于多進程,GIL就無法限制,多個進程可以再多個CPU上運行,充分利用多核優勢。事情往往是相對的,雖然可以充分利用多核優勢,但是進程之間的切換卻比線程的切換代價更高。
所以選擇多線程還是多進程,主要還是看怎樣權衡代價,什么樣的情況。
1、CPU密集代碼
下面來利用斐波那契數列模擬CPU密集運算。
def fib(n):
# 求斐波那契數列的第n個值
if n<=2:
return 1
return fib(n-1)+fib(n-2)
<1>、多進程
打印第25到35個斐波那契數,并計算程序運行時間
import time
from concurrent.futures import ThreadPoolExecutor, as_completed
from concurrent.futures import ProcessPoolExecutor
def fib(n):
if n<=2:
return 1
return fib(n-1)+fib(n-2)
if __name__ == "__main__":
with ProcessPoolExecutor(3) as executor: # 使用進程池控制 每次執行3個進程
all_task = [executor.submit(fib, (num)) for num in range(25,35)]
start_time = time.time()
for future in as_completed(all_task):
data = future.result()
print("exe result: {}".format(data))
print("last time is: {}".format(time.time()-start_time))
# 輸出
exe result: 75025
exe result: 121393
exe result: 196418
exe result: 317811
exe result: 514229
exe result: 832040
exe result: 1346269
exe result: 2178309
exe result: 3524578
exe result: 5702887
last time is: 4.457437038421631
輸出結果,每次打印三個exe result,總重打印十個結果,多進程運行時間為4.45秒
<2>、多線程
import time
from concurrent.futures import ThreadPoolExecutor, as_completed
from concurrent.futures import ProcessPoolExecutor
def fib(n):
if n<=2:
return 1
return fib(n-1)+fib(n-2)
if __name__ == "__main__":
with ThreadPoolExecutor(3) as executor: # 使用線程池控制 每次執行3個線程
all_task = [executor.submit(fib, (num)) for num in range(25,35)]
start_time = time.time()
for future in as_completed(all_task):
data = future.result()
print("exe result: {www.gcyl152.com/ }".format(data))
print("last time is: {}".format(time.time()-start_time))
# 輸出
exe result: 121393
exe result: 75025
exe result: 196418
exe result: 317811
exe result: 514229
exe result: 832040
exe result: 1346269
exe result: 2178309
exe result: 3524578
exe result: 5702887
last time is: 7.3467772006988525
最終程序運行時間為7.34秒
程序的執行之間與計算機的性能有關,每天計算機的執行時間都會有差異。從上述結果中看顯然多線程比多進程要耗費時間。這就是因為對于密集代碼(密集運算,循環語句等),tick計數很快達到100,GIL來回的釋放競爭,線程之間頻繁切換,所以對于密集代碼的執行中,多線程性能不如對進程。
第一步導入依賴
implementation 'com.squareup.retrofit2:adapter-rxjava2:2.4.0'
implementation 'com.squareup.retrofit2:converter-gson:2.4.0'
implementation 'io.reactivex.rxjava2:rxandroid:2.1.0'
implementation 'cn.bingoogolapple:bga-banner:2.2.4@aar'
創建BaseService
import java.util.Map;
import io.reactivex.Observable;
import okhttp3.ResponseBody;
import retrofit2.http.GET;
import retrofit2.http.POST;
import retrofit2.http.QueryMap;
import retrofit2.http.Url;
public interface BaseService {
@GET
Observable<ResponseBody> get(@Url String url, @QueryMap Map<String, String> map);
@POST
Observable<ResponseBody>www.gcyL157.com post(@Url www.michenggw.com String url, @QueryMap Map<String, String> map);
工具類
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import io.reactivex.Observer;
import io.reactivex.www.yigouyule2.cn android.schedulers.AndroidSchedulers;
import io.reactivex.disposables.Disposable;
import io.reactivex.schedulers.Schedulers;
import okhttp3.ResponseBody;
import retrofit2.Retrofit;
import retrofit2.adapter.rxjava2.RxJava2CallAdapterFactory;
public class HttpHelper11 www.mhylpt.com{
private BaseService mBaseService;
public HttpHelper11(){
Retrofit retrofit=new Retrofit.Builder()
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
.baseUrl("http://www.zhaoapi.cn/")
.build();
mBaseService=retrofit.create(BaseService.class);
}
//get請求
public HttpHelper11 get(String url, Map<String,String> map){
if(map==null){
map=new HashMap<>();
}
mBaseService.get(url,map)
.subscribeOn(Schedulers.io(www.mcyllpt.com/))
.observeOn(AndroidSchedulers.mainThread())
.subscribe(observer);
return this;
}
//post請求
public HttpHelper11 post(String url, Map<String,String> map){
if(map==null){
map=new HashMap<www.dfgjyl.cn>();
}
mBaseService.post(url,map)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(observer);
return this;
}
private Observer observer=new Observer<ResponseBody>() {
@Override
public void onSubscribe(Disposable d) {
}
@Override
public void onNext(ResponseBody responseBody) {
try {
String data= responseBody.string();
listener.success(data);
} catch (IOException e) {
e.printStackTrace(www.365soke.com);
}
}
@Override
public void onError(Throwable e) {
String error= e.getMessage();
listener.fail(error);
}
@Override
public void onComplete() {
}
};
private HttpListener listener;
public void result(HttpListener listener){
this.listener=listener;
}
public interface HttpListener {
void success(String data);
void fail(String error);
}
}