python等待函數(shù)完成,python等待返回結(jié)果再進(jìn)行下一步

python 怎么讓程序接受ctrl + c終止信號(hào)

花了一天時(shí)間用python為服務(wù)寫了個(gè)壓力測試。很簡單,多線程向服務(wù)器發(fā)請求。但寫完之后發(fā)現(xiàn)如果中途想停下來,按Ctrl+C達(dá)不到效果,自然想到要用信號(hào)處理函數(shù)捕捉信號(hào),使線程都停下來,問題解決的方法請往下看:

成都創(chuàng)新互聯(lián)長期為上1000+客戶提供的網(wǎng)站建設(shè)服務(wù),團(tuán)隊(duì)從業(yè)經(jīng)驗(yàn)10年,關(guān)注不同地域、不同群體,并針對不同對象提供差異化的產(chǎn)品和服務(wù);打造開放共贏平臺(tái),與合作伙伴共同營造健康的互聯(lián)網(wǎng)生態(tài)環(huán)境。為海北州企業(yè)提供專業(yè)的網(wǎng)站設(shè)計(jì)制作、成都做網(wǎng)站,海北州網(wǎng)站改版等技術(shù)服務(wù)。擁有十多年豐富建站經(jīng)驗(yàn)和眾多成功案例,為您定制開發(fā)。

復(fù)制代碼代碼如下:

#!/bin/env python

# -*- coding: utf-8 -*-

#filename: peartest.py

import threading, signal

is_exit = False

def doStress(i, cc):

global is_exit

idx = i

while not is_exit:

if (idx 10000000):

print "thread[%d]: idx=%d"%(i, idx)

idx = idx + cc

else:

break

print "thread[%d] complete."%i

def handler(signum, frame):

global is_exit

is_exit = True

print "receive a signal %d, is_exit = %d"%(signum, is_exit)

if __name__ == "__main__":

signal.signal(signal.SIGINT, handler)

signal.signal(signal.SIGTERM, handler)

cc = 5

for i in range(cc):

t = threading.Thread(target=doStress, args=(i,cc))

t.start()

上面是一個(gè)模擬程序,并不真正向服務(wù)發(fā)送請求,而代之以在一千萬以內(nèi),每個(gè)線程每隔并發(fā)數(shù)個(gè)(cc個(gè))打印一個(gè)整數(shù)。很明顯,當(dāng)所有線程都完成自己的任務(wù)后,進(jìn)程會(huì)正常退出。但如果我們中途想退出(試想一個(gè)壓力測試程序,在中途已經(jīng)發(fā)現(xiàn)了問題,需要停止測試),該腫么辦?你當(dāng)然可以用ps查找到進(jìn)程號(hào),然后kill -9殺掉,但這樣太繁瑣了,捕捉Ctrl+C是最自然的想法。上面示例程序中已經(jīng)捕捉了這個(gè)信號(hào),并修改全局變量is_exit,線程中會(huì)檢測這個(gè)變量,及時(shí)退出。

但事實(shí)上這個(gè)程序并不work,當(dāng)你按下Ctrl+C時(shí),程序照常運(yùn)行,并無任何響應(yīng)。網(wǎng)上搜了一些資料,明白是python的子線程如果不是daemon的話,主線程是不能響應(yīng)任何中斷的。但設(shè)為daemon后主線程會(huì)隨之退出,接著整個(gè)進(jìn)程很快就退出了,所以還需要在主線程中檢測各個(gè)子線程的狀態(tài),直到所有子線程退出后自己才退出,因此上例29行之后的代碼可以修改為:

復(fù)制代碼代碼如下:

threads=[]

for i in range(cc):

t = threading.Thread(target=doStress, args=(i, cc))

t.setDaemon(True)

threads.append(t)

t.start()

for i in range(cc):

threads[i].join()

重新試一下,問題依然沒有解決,進(jìn)程還是沒有響應(yīng)Ctrl+C,這是因?yàn)閖oin()函數(shù)同樣會(huì)waiting在一個(gè)鎖上,使主線程無法捕獲信號(hào)。因此繼續(xù)修改,調(diào)用線程的isAlive()函數(shù)判斷線程是否完成:

復(fù)制代碼代碼如下:

while 1:

alive = False

for i in range(cc):

alive = alive or threads[i].isAlive()

if not alive:

break

這樣修改后,程序完全按照預(yù)想運(yùn)行了:可以順利的打印每個(gè)線程應(yīng)該打印的所有數(shù)字,也可以中途用Ctrl+C終結(jié)整個(gè)進(jìn)程。完整的代碼如下:

復(fù)制代碼代碼如下:

#!/bin/env python

# -*- coding: utf-8 -*-

#filename: peartest.py

import threading, signal

is_exit = False

def doStress(i, cc):

global is_exit

idx = i

while not is_exit:

if (idx 10000000):

print "thread[%d]: idx=%d"%(i, idx)

idx = idx + cc

else:

break

if is_exit:

print "receive a signal to exit, thread[%d] stop."%i

else:

print "thread[%d] complete."%i

def handler(signum, frame):

global is_exit

is_exit = True

print "receive a signal %d, is_exit = %d"%(signum, is_exit)

if __name__ == "__main__":

signal.signal(signal.SIGINT, handler)

signal.signal(signal.SIGTERM, handler)

cc = 5

threads = []

for i in range(cc):

t = threading.Thread(target=doStress, args=(i,cc))

t.setDaemon(True)

threads.append(t)

t.start()

while 1:

alive = False

for i in range(cc):

alive = alive or threads[i].isAlive()

if not alive:

break

其實(shí),如果用python寫一個(gè)服務(wù),也需要這樣,因?yàn)樨?fù)責(zé)服務(wù)的那個(gè)線程是永遠(yuǎn)在那里接收請求的,不會(huì)退出,而如果你想用Ctrl+C殺死整個(gè)服務(wù),跟上面的壓力測試程序是一個(gè)道理。總結(jié)一下,python多線程中要響應(yīng)Ctrl+C的信號(hào)以殺死整個(gè)進(jìn)程,需要:

1.把所有子線程設(shè)為Daemon;

2.使用isAlive()函數(shù)判斷所有子線程是否完成,而不是在主線程中用join()函數(shù)等待完成;

3.寫一個(gè)響應(yīng)Ctrl+C信號(hào)的函數(shù),修改全局變量,使得各子線程能夠檢測到,并正常退出。

Python異步編程全攻略

如果你厭倦了多線程,不妨試試python的異步編程,再引入async, await關(guān)鍵字之后語法變得更加簡潔和直觀,又經(jīng)過幾年的生態(tài)發(fā)展,現(xiàn)在是一個(gè)很不錯(cuò)的并發(fā)模型。

下面介紹一下python異步編程的方方面面。

因?yàn)镚IL的存在,所以Python的多線程在CPU密集的任務(wù)下顯得無力,但是對于IO密集的任務(wù),多線程還是足以發(fā)揮多線程的優(yōu)勢的,而異步也是為了應(yīng)對IO密集的任務(wù),所以兩者是一個(gè)可以相互替代的方案,因?yàn)樵O(shè)計(jì)的不同,理論上異步要比多線程快,因?yàn)楫惒降幕ㄤN更少, 因?yàn)椴恍枰~外系統(tǒng)申請額外的內(nèi)存,而線程的創(chuàng)建跟系統(tǒng)有關(guān),需要分配一定量的內(nèi)存,一般是幾兆,比如linux默認(rèn)是8MB。

雖然異步很好,比如可以使用更少的內(nèi)存,比如更好地控制并發(fā)(也許你并不這么認(rèn)為:))。但是由于async/await 語法的存在導(dǎo)致與之前的語法有些割裂,所以需要適配,需要付出額外的努力,再者就是生態(tài)遠(yuǎn)遠(yuǎn)沒有同步編程強(qiáng)大,比如很多庫還不支持異步,所以你需要一些額外的適配。

為了不給其他網(wǎng)站帶來困擾,這里首先在自己電腦啟動(dòng)web服務(wù)用于測試,代碼很簡單。

本文所有依賴如下:

所有依賴可通過代碼倉庫的requirements.txt一次性安裝。

首先看一個(gè)錯(cuò)誤的例子

輸出如下:

發(fā)現(xiàn)花費(fèi)了3秒,不符合預(yù)期呀。。。。這是因?yàn)殡m然用了協(xié)程,但是每個(gè)協(xié)程是串行的運(yùn)行,也就是說后一個(gè)等前一個(gè)完成之后才開始,那么這樣的異步代碼并沒有并發(fā),所以我們需要讓這些協(xié)程并行起來

為了讓代碼變動(dòng)的不是太多,所以這里用了一個(gè)笨辦法來等待所有任務(wù)完成, 之所以在main函數(shù)中等待是為了不讓ClientSession關(guān)閉, 如果你移除了main函數(shù)中的等待代碼會(huì)發(fā)現(xiàn)報(bào)告異常 RuntimeError: Session is closed ,而代碼里的解決方案非常的不優(yōu)雅,需要手動(dòng)的等待,為了解決這個(gè)問題,我們再次改進(jìn)代碼。

這里解決的方式是通過 asyncio.wait 方法等待一個(gè)協(xié)程列表,默認(rèn)是等待所有協(xié)程結(jié)束后返回,會(huì)返回一個(gè)完成(done)列表,以及一個(gè)待辦(pending)列表。

如果我們不想要協(xié)程對象而是結(jié)果,那么我們可以使用 asyncio.gather

結(jié)果輸出如下:

通過 asyncio.ensure_future 我們就能創(chuàng)建一個(gè)協(xié)程,跟調(diào)用一個(gè)函數(shù)差別不大,為了等待所有任務(wù)完成之后退出,我們需要使用 asyncio.wait 等方法來等待,如果只想要協(xié)程輸出的結(jié)果,我們可以使用 asyncio.gather 來獲取結(jié)果。

雖然前面能夠隨心所欲的創(chuàng)建協(xié)程,但是就像多線程一樣,我們也需要處理協(xié)程之間的同步問題,為了保持語法及使用情況的一致,多線程中用到的同步功能,asyncio中基本也能找到, 并且用法基本一致,不一致的地方主要是需要用異步的關(guān)鍵字,比如 async with/ await 等

通過鎖讓并發(fā)慢下來,讓協(xié)程一個(gè)一個(gè)的運(yùn)行。

輸出如下:

通過觀察很容易發(fā)現(xiàn),并發(fā)的速度因?yàn)殒i而慢下來了,因?yàn)槊看沃挥幸粋€(gè)協(xié)程能獲得鎖,所以并發(fā)變成了串行。

通過事件來通知特定的協(xié)程開始工作,假設(shè)有一個(gè)任務(wù)是根據(jù)http響應(yīng)結(jié)果選擇是否激活。

輸出如下:

可以看到事件(Event)等待者都是在得到響應(yīng)內(nèi)容之后輸出,并且事件(Event)可以是多個(gè)協(xié)程同時(shí)等待。

上面的事件雖然很棒,能夠在不同的協(xié)程之間同步狀態(tài),并且也能夠一次性同步所有的等待協(xié)程,但是還不夠精細(xì)化,比如想通知指定數(shù)量的等待協(xié)程,這個(gè)時(shí)候Event就無能為力了,所以同步原語中出現(xiàn)了Condition。

輸出如下:

可以看到,前面兩個(gè)等待的協(xié)程是在同一時(shí)刻完成,而不是全部等待完成。

通過創(chuàng)建協(xié)程的數(shù)量來控制并發(fā)并不是非常優(yōu)雅的方式,所以可以通過信號(hào)量的方式來控制并發(fā)。

輸出如下:

可以發(fā)現(xiàn),雖然同時(shí)創(chuàng)建了三個(gè)協(xié)程,但是同一時(shí)刻只有兩個(gè)協(xié)程工作,而另外一個(gè)協(xié)程需要等待一個(gè)協(xié)程讓出信號(hào)量才能運(yùn)行。

無論是協(xié)程還是線程,任務(wù)之間的狀態(tài)同步還是很重要的,所以有了應(yīng)對各種同步機(jī)制的同步原語,因?yàn)橐WC一個(gè)資源同一個(gè)時(shí)刻只能一個(gè)任務(wù)訪問,所以引入了鎖,又因?yàn)樾枰粋€(gè)任務(wù)等待另一個(gè)任務(wù),或者多個(gè)任務(wù)等待某個(gè)任務(wù),因此引入了事件(Event),但是為了更精細(xì)的控制通知的程度,所以又引入了條件(Condition), 通過條件可以控制一次通知多少的任務(wù)。

有時(shí)候的并發(fā)需求是通過一個(gè)變量控制并發(fā)任務(wù)的并發(fā)數(shù)而不是通過創(chuàng)建協(xié)程的數(shù)量來控制并發(fā),所以引入了信號(hào)量(Semaphore),這樣就可以在創(chuàng)建的協(xié)程數(shù)遠(yuǎn)遠(yuǎn)大于并發(fā)數(shù)的情況下讓協(xié)程在指定的并發(fā)量情況下并發(fā)。

不得不承認(rèn)異步編程相比起同步編程的生態(tài)要小的很多,所以不可能完全異步編程,因此需要一種方式兼容。

多線程是為了兼容同步得代碼。

多進(jìn)程是為了利用CPU多核的能力。

輸出如下:

可以看到總耗時(shí)1秒,說明所有的線程跟進(jìn)程是同時(shí)運(yùn)行的。

下面是本人使用過的一些異步庫,僅供參考

web框架

http客戶端

數(shù)據(jù)庫

ORM

雖然異步庫發(fā)展得還算不錯(cuò),但是中肯的說并沒有覆蓋方方面面。

雖然我鼓勵(lì)大家嘗試異步編程,但是本文的最后卻是讓大家謹(jǐn)慎的選擇開發(fā)環(huán)境,如果你覺得本文的并發(fā),同步,兼容多線程,多進(jìn)程不值得一提,那么我十分推薦你嘗試以異步編程的方式開始一個(gè)新的項(xiàng)目,如果你對其中一些還有疑問或者你確定了要使用的依賴庫并且大多數(shù)是沒有異步庫替代的,那么我還是建議你直接按照自己擅長的同步編程開始。

異步編程雖然很不錯(cuò),不過,也許你并不需要。

python 在線程函數(shù)中如何實(shí)現(xiàn)線程的暫停、恢復(fù)和終止?

我們都知道python中可以是threading模塊實(shí)現(xiàn)多線程, 但是模塊并沒有提供暫停, 恢復(fù)和停止線程的方法, 一旦線程對象調(diào)用start方法后, 只能等到對應(yīng)的方法函數(shù)運(yùn)行完畢. 也就是說一旦start后, 線程就屬于失控狀態(tài). 不過, 我們可以自己實(shí)現(xiàn)這些. 一般的方法就是循環(huán)地判斷一個(gè)標(biāo)志位, 一旦標(biāo)志位到達(dá)到預(yù)定的值, 就退出循環(huán). 這樣就能做到退出線程了. 但暫停和恢復(fù)線程就有點(diǎn)難了, 我一直也不清除有什么好的方法

python如何設(shè)計(jì)一個(gè)函數(shù),實(shí)現(xiàn)等待用戶輸入數(shù)字,超時(shí)則默認(rèn)選擇,

很簡單,新建一個(gè)線程即可

import threading

def input_func( context ):

context[ 'data' ] = input( 'input:' )

context = { 'data' : 'default' }

t = threading.Thread( target = input_func ,args = ( context , ) )

t.start( )

t.join( 10 ) #等待10秒

print( context )

python recv函數(shù)會(huì)一直等待嗎

d = s.recv(1024)

這段代碼的返回值通過使用 not d 判斷總是失敗,于是那個(gè)循環(huán)讀取的 while 就陷入了死循環(huán),請各位幫忙看一下,謝謝!

#-*- coding:utf8 -*-

import socket

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

s.connect(("", 80))

s.send("GET / HTTP/1.1\r\nHost:\r\n\r\n")

tmp = []

while True:

d = s.recv(1024)

if not d:

break

tmp.append(d)

data = ''.join(tmp)

s.close()

header, html = data.split("\r\n\r\n", 1)

print header

with open("oschina.html", "wb") as f:

f.write(html)

python中怎么實(shí)現(xiàn) 必須執(zhí)行完一個(gè)函數(shù)才能執(zhí)行下一個(gè)函數(shù)

簡答來說:通過外部的一個(gè)變量

T=fasle

def regist():

""" 注冊"""

print “注冊”

T=true

def login():

""" 登陸"""

if not T:

print "先注冊"

return

print “登陸成功”

def logout():

""" 注銷"""

T=fasle

一般的話

注冊后都會(huì)在數(shù)據(jù)庫中 記錄注冊信息

登陸的時(shí)候 先去到數(shù)據(jù)庫中查看是否有 沒有返回空 有的話返回注冊信息 ,比如登陸密碼 用于下步的密碼核對

分享文章:python等待函數(shù)完成,python等待返回結(jié)果再進(jìn)行下一步
文章鏈接:http://muchs.cn/article38/phedsp.html

成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供外貿(mào)建站、外貿(mào)網(wǎng)站建設(shè)、移動(dòng)網(wǎng)站建設(shè)、做網(wǎng)站、微信公眾號(hào)服務(wù)器托管

廣告

聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請盡快告知,我們將會(huì)在第一時(shí)間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場,如需處理請聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來源: 創(chuàng)新互聯(lián)

綿陽服務(wù)器托管