1. 程式人生 > >Python--進程與線程

Python--進程與線程

red 主線程 bsp sleep 並行 logs 管理 conf 明顯

一,回顧操作系統的概念

操作系統位於底層硬件與應用軟件之間的一層

工作方式:向下管理軟件,向上提供接口

二,進程線程的概念

  進程是一個資源單位,線程是一個最小的執行單位

    一個線程只能屬於一個進程,而一個進程可以有多個線程,但至少有一個線程

三,並行與並發

並行:
就是有多個進程可以同時運行的叫做並行
並發:
就是在一個處理器的情況下,切換執行,叫做並發

python無法實現並行處理,因為全局解釋器鎖gil導致同一時刻同一進程
只能有一個線程被運行。

GIL全局解釋器鎖
但是不影響Python開多進程

多線程代碼示例

import threading
import
time ‘‘‘ 程序在運行是有一個主線程,當程序開啟多線程的時候,主線程依舊會執行, 主線程執行到最後時,並沒有結束,而是在等待子線程的結束後主線程結束 ‘‘‘ def misc(): print("聽歌") time.sleep(3) print("聽歌結束") def xieboke(): print("寫博客") time.sleep(5) print("寫博客結束") #開啟線程 t1=threading.Thread(target=misc)#t1,t2是一個線程對象 t2=threading.Thread(target=xieboke) t1.start() t2.start()
print("主線程")



#開啟多線程的另一種方式

import threading
import time
class MyThread(threading.Thread):
    ‘‘‘
    用類的繼承,繼承線程的方法開啟線程
    ‘‘‘

    def __init__(self,num):
    ‘‘‘
    繼承父類的__init__方法
    ‘‘‘
        threading.Thread.__init__(self)
        self.num=num

    def run(self):
        print("running on mythread:%s
"%self.num) time.sleep(3) print("end%s"%self.num) t1=MyThread(10) t2=MyThread(20) t1.start() t2.start() print("主線程")



jion的使用
t.jion方法會阻塞主進程的運行,但不會影響其他線程的運行

setDaemon方法
-守護線程
當某個線程設置為守護線程的時候,它會隨著主線程的結束而結束
t.setDaemon(True)

線程對象下的幾個方法:
-isAlive()檢測線程是否活動,返回值是布爾值
-getName():返回線程名
-setName():設置線程名稱

threading模塊提供的一些方法:
threading.currentTread():返回當前線程變量
threading.enumerate():返回一個包含正在運行的線程的list。
threading.activeCount():返回正在運行的線程數量

Python對於計算密集型運行比較慢,效率低;對於IO密集型效率有明顯提高

Python多線程
互斥鎖:
互斥鎖的意義就是在保護鎖內代碼同一時間只有一個線程在使用
直到代碼執行完成,解鎖後其他線程才能執行所內代碼。
使用格式:
-lock=threading.Lock()創建一把鎖的對象
lock.acquire()#加鎖
....需要保護的執行語句
lock.release()#解鎖

死鎖與遞歸鎖
代碼示例:

       import threading
        import time

        muteA=threading.Lock()
        muteB=threading.Lock()

        class MyThread(threading.Thread):
            def __init__(self):
                threading.Thread.__init__(self)

            def run(self):
                self.func1()
                self.func2()

            def func1(self):
                muteA.acquire()
                print("鎖A執行內容",MyThread.getName(self))
                muteB.acquire()
                print("鎖B執行內容",MyThread.getName(self))
                muteB.release()
                muteA.release()

            def func2(self):
                muteB.acquire()
                print("第二個函數的鎖B",MyThread.getName(self))
                muteA.acquire()
                print("第二個函數的鎖A",MyThread.getName(self))
                muteA.release()
                muteB.release()

        if __name__=="__main__":
            for i in range(10):
                my_thread=MyThread()
                my_thread.start()



形成死鎖的原因在於當線程1在第二個函數中拿到鎖B向下執行需要鎖A的時候,線程2在函數1中
已經拿到的鎖A,在等待線程1釋放B。兩個線程都沒有釋放另一個線程需要的鎖,所以就形成了死鎖。


遞歸鎖的應用
遞歸鎖未避免死鎖的產生,在鎖內實行一個引用計數,當有一把使用是計速器加一,釋放後,去除計數
到代碼在執行鎖內代碼時,如果有其他線程搶鎖,計數如果為零,線程可以拿到鎖,大於零,拒絕線程拿鎖
這樣就能避免鎖的重復,也就不會產生死鎖
代碼示例:
import threading
import time

Rlock=threading.Rlock()

class MyThread(threading.Thread):
def __init__(self):
threading.Thread.__init__(self)

def run(self):
self.func1()
self.func2()

def func1(self):
Rlock.acquire()
print("鎖A執行內容",MyThread.getName(self))
Rlock.acquire()
print("鎖B執行內容",MyThread.getName(self))
Rlock.release()
Rlock.release()

def func2(self):
Rlock.acquire()
print("第二個函數的鎖B",MyThread.getName(self))
Rlock.acquire()
print("第二個函數的鎖A",MyThread.getName(self))
Rlock.release()
Rlock.release()

if __name__=="__main__":
for i in range(10):
my_thread=MyThread()
my_thread.start()

event方法使用:
event方法可以讓兩個線程之間通信,當一個線程需要另一個線程準備數據的時候,
event.wait(),阻塞程序的運行,直到另一個線程將數據準備完成後,使用event.set()
返回一個true值,event.wait()接受到該值之後,線程開始運行。wait方法後可以接一個超時
時間參數,規定在一定時間內阻塞,超時後運行。

import threading
import time
import logging

logging.basicConfig(level=logging.DEBUG, format=‘(%(threadName)-10s) %(message)s‘,)


def worker(event):
logging.debug(‘Waiting for redis ready...‘)

while not event.isSet():
logging.debug("wait.......")
event.wait(3) # if flag=False阻塞,等待flag=true繼續執行


logging.debug(‘redis ready, and connect to redis server and do some work [%s]‘, time.ctime())
time.sleep(1)

def main():

readis_ready = threading.Event() # flag=False創建一個event對象
t1 = threading.Thread(target=worker, args=(readis_ready,), name=‘t1‘)
t1.start()

t2 = threading.Thread(target=worker, args=(readis_ready,), name=‘t2‘)
t2.start()

logging.debug(‘first of all, check redis server, make sure it is OK, and then trigger the redis ready event‘)

time.sleep(6) # simulate the check progress
readis_ready.set() # flag=Ture


if __name__=="__main__":
main()


進程multprocessing模塊

multprocessing模塊與threading模塊使用同一套api,使用方法調用方法與threading模塊一樣

代碼示例:
from multiprocessing import Process

import time

def f(name):
print("hello",name,time.ctime())
time.sleep(1)

if __name__=="__main__":
p_list=[]
for i in range(3):
p=Process(target=f,args=("alvin:%s"%i,))
p_list.append(p)
p.start()


協程的應用:
協程是單線程的,不能切換。因為協程對IO操作的判斷由自己控制
import time

# 可以實現並發


def consumer():

r = ‘‘
while True:

n = yield r
if not n:
return
print(‘[CONSUMER] ←← Consuming %s...‘ % n)
time.sleep(1)
r = ‘200 OK‘

def produce(c):

next(c)
n = 0
while n < 5:
n = n + 1
print(‘[PRODUCER] →→ Producing %s...‘ % n)

cr = c.send(n)

print(‘[PRODUCER] Consumer return: %s‘ % cr)

c.close()

if __name__==‘__main__‘:

c = consumer()
produce(c)

gevent模塊的使用:
from gevent import monkey
monkey.patch_all()

import gevent
from urllib import request
import time

def f(url):
print(‘GET: %s‘ % url)
resp = request.urlopen(url)
data = resp.read()
print(‘%d bytes received from %s.‘ % (len(data), url))

start=time.time()

gevent.joinall([
gevent.spawn(f, ‘https://itk.org/‘),
gevent.spawn(f, ‘https://www.github.com/‘),
gevent.spawn(f, ‘https://zhihu.com/‘),
])

#f(‘https://itk.org/‘)
#f(‘https://www.github.com/‘)
#f(‘https://zhihu.com/‘)


print(time.time()-start)  

Python--進程與線程