Python學習筆記之 進程和線程
多任務(wù)的原理
-
現(xiàn)代操作系統(tǒng)(Windows巨缘、Mac OS X、Linux采呐、UNIX等)都支持“多任務(wù)”
什么叫多任務(wù)若锁??斧吐?
操作系統(tǒng)同時可以運行多個任務(wù)單核CPU實現(xiàn)多任務(wù)原理:操作系統(tǒng)輪流讓各個任務(wù)交替執(zhí)行又固,QQ執(zhí)行2us,切換到微信会通,在執(zhí)行2us口予,再切換到陌陌,執(zhí)行2us……涕侈。表面是看沪停,每個任務(wù)反復執(zhí)行下去,但是CPU調(diào)度執(zhí)行速度太快了裳涛,導致我們感覺就行所有任務(wù)都在同時執(zhí)行一樣
多核CPU實現(xiàn)多任務(wù)原理:真正的秉性執(zhí)行多任務(wù)只能在多核CPU上實現(xiàn)木张,但是由于任務(wù)數(shù)量遠遠多于CPU的核心數(shù)量,所以端三,操作西永也會自動把很多任務(wù)輪流調(diào)度到每個核心上執(zhí)行
并發(fā):看上去一起執(zhí)行舷礼,任務(wù)書多于CPU核心數(shù)
并行:真正一起執(zhí)行,任務(wù)數(shù)小于等于CPU核心數(shù)實現(xiàn)多任務(wù)的方式:
1郊闯、多進程模式
2妻献、多線程模式
3蛛株、協(xié)程模式
4、多進程+多線程模式
一育拨、進程
對于操作系統(tǒng)而言谨履,一個任務(wù)就是一個進程
進程是系統(tǒng)中程序執(zhí)行和資源分配的基本單位。每個進程都有自己的數(shù)據(jù)段熬丧、代碼段笋粟、和堆棧段
單任務(wù)現(xiàn)象
from time import sleep
def run():
while True:
print("sunck is a nice man")
sleep(1.2)
if __name__ == "__main__":
while True:
print("sunck is a good man")
sleep(1)
# 不會執(zhí)行到run方法,只有上面的while循環(huán)結(jié)束才可以執(zhí)行
run()
啟動進程實現(xiàn)多任務(wù)
'''
multiprocessing 庫
跨平臺版本的多進程模塊析蝴,提供了一個Process類來代表一個進程對象
'''
from multiprocessing import Process
from time import sleep
import os
#子進程需要執(zhí)行的代碼
def run(str):
while True:
# os.getpid()獲取當前進程id號
# os.getppid()獲取當前進程的父進程id號
print("sunck is a %s man--%s--%s"%(str, os.getpid(),os.getppid()))
sleep(1.2)
if __name__ == "__main__":
print("主(父)進程啟動-%s"%(os.getpid()))
#創(chuàng)建子進程
#target說明進程執(zhí)行的任務(wù)
p = Process(target=run, args=("nice",))
#啟動進程
p.start()
while True:
print("sunck is a good man")
sleep(1)
父子進程的先后順序
from multiprocessing import Process
from time import sleep
import os
def run(str):
print("子進程啟動")
sleep(3)
print("子進程結(jié)束")
if __name__ == "__main__":
print("父進程啟動")
p = Process(target=run, args=("nice",))
p.start()
#父進程的結(jié)束不能影響子進程害捕,讓父進程等待子進程結(jié)束再執(zhí)行父進程
p.join()
print("父進程結(jié)束")
全局變量在多個進程中不能共享
from multiprocessing import Process
from time import sleep
num = 100
def run():
print("子進程開始")
global num # num = 100
num += 1
print(num)
print("子進程結(jié)束")
if __name__ == "__main__":
print("父進程開始")
p = Process(target=run)
p.start()
p.join()
# 在子進程中歐修改全局變量對父進程中的全局變量沒有影響
# 在創(chuàng)建子進程時對全局變量做了一個備份,父進程中的與子進程中的num是完全不同的兩個變量
print("父進程結(jié)束--%d"%num)
啟動大量子進程
from multiprocessing import Pool
import os, time, random
def run(name):
print("子進程%d啟動--%s" % (name, os.getpid()))
start = time.time()
time.sleep(random.choice([1,2,3]))
end = time.time()
print("子進程%d結(jié)束--%s--耗時%.2f" % (name, os.getpid(), end-start))
if __name__ == "__main__":
print("父進程啟動")
#創(chuàng)建多個進程
#進程池
#表示可以同時執(zhí)行的進程數(shù)量
#Pool默認大小是CPU核心數(shù)
pp = Pool(2)
for i in range(3):
#創(chuàng)建進程闷畸,放入進程池同意管理
pp.apply_async(run,args=(i,))
#在調(diào)用join之前必須先調(diào)用close,調(diào)用close之后就不能再繼續(xù)添加新的進程了
pp.close()
#進程池對象調(diào)用join尝盼,會等待進程池中所有的子進程結(jié)束完畢再去執(zhí)行父進程
pp.join()
print("父進程結(jié)束")
拷貝文件
import os, time
from multiprocessing import Pool
#實現(xiàn)文件的拷貝
def copyFile(rPath, wPath):
fr = open(rPath, "rb")
fw = open(wPath, "wb")
context = fr.read()
fw.write(context)
fr.close()
fw.close()
path = r"C:\Users\xlg\Desktop\Python-1704\day20\2、進程\file"
toPath = r"C:\Users\xlg\Desktop\Python-1704\day20\2腾啥、進程\toFile"
#讀取path下的都有的文件
filesList = os.listdir(path)
#啟動for循環(huán)處理每一個文件
start = time.time()
for fileName in filesList:
copyFile(os.path.join(path,fileName), os.path.join(toPath,fileName))
end = time.time()
print("總耗時:%0.2f" % (end-start))
多進程實現(xiàn)文件拷貝
import os, time
from multiprocessing import Pool
#實現(xiàn)文件的拷貝
def copyFile(rPath, wPath):
fr = open(rPath, "rb")
fw = open(wPath, "wb")
context = fr.read()
fw.write(context)
fr.close()
fw.close()
path = r"C:\Users\xlg\Desktop\Python-1704\day20\2东涡、進程\file"
toPath = r"C:\Users\xlg\Desktop\Python-1704\day20\2、進程\toFile"
if __name__ == "__main__":
# 讀取path下的都有的文件
filesList = os.listdir(path)
start = time.time()
pp = Pool(4)
for fileName in filesList:
pp.apply_async(copyFile, args=(os.path.join(path,fileName), os.path.join(toPath,fileName)))
pp.close()
pp.join()
end = time.time()
print("總耗時:%0.2f" % (end-start))
封裝進程對象
''' 創(chuàng)建一個類文件'''
from multiprocessing import Process
import os, time
class SunckProcess(Process):
def __init__(self,name):
Process.__init__(self)
self.name = name
def run(self):
print("子進程(%s-%s)啟動" % (self.name, os.getpid()))
#子進程的功能
time.sleep(3)
print("子進程(%s-%s)結(jié)束" % (self.name, os.getpid()))
from sunckProcess import SunckProcess
if __name__ == "__main__":
print("父進程啟動")
#創(chuàng)建子進程
p = SunckProcess("test")
# 自動調(diào)用p進程對象的run方法
p.start()
p.join()
print("父進程結(jié)束")
進程間通信
from multiprocessing import Process, Queue
import os, time
def write(q):
print("啟動寫子進程%s" % (os.getpid()))
for chr in ["A", "B", "C", "D"]:
q.put(chr)
time.sleep(1)
print("結(jié)束寫子進程%s" % (os.getpid()))
def read(q):
print("啟動讀子進程%s" % (os.getpid()))
while True:
value = q.get(True)
print("value = " + value)
print("結(jié)束讀子進程%s" % (os.getpid()))
if __name__ == "__main__":
#父進程創(chuàng)建隊列倘待,并傳遞給子進程
q = Queue()
pw = Process(target=write, args=(q,))
pr = Process(target=read, args=(q,))
pw.start()
pr.start()
#
pw.join()
#pr進程里是個死循環(huán),無法等待其結(jié)束组贺,只能強行結(jié)束
pr.terminate()
print("父進程結(jié)束")
二凸舵、線程
-
在一個進程的內(nèi)部,要同時干多件事失尖,就需要同時運行多個“子任務(wù)”啊奄,我們把進程內(nèi)的這些“子任務(wù)”叫做線程
線程通常叫做輕型的進程。線程是共享內(nèi)存空間的并發(fā)執(zhí)行的多任務(wù)掀潮,每一個線程都共享一個進程的資源
線程是最小的執(zhí)行單元菇夸,而進程由至少一個線程組成。如何調(diào)度進程和線程仪吧,完全由操作系統(tǒng)絕對庄新,程序自己不能決定什么時候執(zhí)行,執(zhí)行多長時間
模塊
1薯鼠、_thread模塊 低級模塊
2择诈、threading模塊 高級模塊,對_thread進行了封裝
啟動一個線程
import threading,time
def run(num):
print("子線程(%s)開始" % (threading.current_thread().name))
#實現(xiàn)線程的功能
time.sleep(2)
print("打印", num)
time.sleep(2)
print("子線程(%s)結(jié)束" % (threading.current_thread().name))
if __name__ == "__main__":
#任何進程默認就會啟動一個線程出皇,稱為主線程羞芍,主線程可以啟動新的子線程
#current_thread():返回返回當前線程的實例
print("主線程(%s)啟動" % (threading.current_thread().name))
#創(chuàng)建子線程 線程的名稱
t = threading.Thread(target=run, name="runThread", args=(1,))
t.start()
#等待線程結(jié)束
t.join()
print("主線程(%s)結(jié)束" % (threading.current_thread().name))
線程間共享數(shù)據(jù)
import threading
'''
多線程和多進程最大的不同在于,多進程中郊艘,同一個變量荷科,各自有一份拷貝存在每個進程中唯咬,互不影響。而多線程中畏浆,所有變量都由所有線程共享副渴。所以,任何一個變量都可以被任意一個線程修改全度,因此煮剧,線程之間共享數(shù)據(jù)最大的危險在于多個線程同時修改一個變量,容易把內(nèi)容改亂了将鸵。
'''
num = 0
def run(n):
global num
for i in range(10000000):
num = num + n # 15 = 9 + 6
num = num - n # 9
if __name__ == "__main__":
t1 = threading.Thread(target=run, args=(6,))
t2 = threading.Thread(target=run, args=(9,))
t1.start()
t2.start()
t1.join()
t2.join()
print("num =",num)
'''
線程1 num = num + 6
num - 6 = 3
線程2 num = num + 9
3 - 9 = -6
'''
線程鎖解決數(shù)據(jù)混亂
'''
兩個線程同時工作勉盅,一個存錢,一個取錢
可能導致數(shù)據(jù)異常
思路:加鎖顶掉,
'''
import threading
#鎖對象
lock = threading.Lock()
num = 0
def run(n):
global num
for i in range(10000000):
# 鎖
# 確保了這段代碼只能由一個線程從頭到尾的完整執(zhí)行
# 阻止了多線程的并發(fā)執(zhí)行草娜,包含鎖的某段代碼實際上只能以單線程模式執(zhí)行,所以效率大大滴降低了
# 由于可以存在多個鎖痒筒,不同線程持有不同的鎖宰闰,并試圖獲取其他的鎖,可能造成死鎖簿透,導致多個線程掛起移袍。只能靠操作系統(tǒng)強制終止
'''
lock.acquire()
try:
num = num + n # 15 = 9 + 6
num = num - n # 9
finally:
#修改完一定要釋放鎖
lock.release()
'''
#與上面代碼功能相同,with lock可以自動上鎖與解鎖
with lock:
num = num + n
num = num - n
if __name__ == "__main__":
t1 = threading.Thread(target=run, args=(6,))
t2 = threading.Thread(target=run, args=(9,))
t1.start()
t2.start()
t1.join()
t2.join()
print("num =",num)
ThreadLocal.py
import threading
num = 0
#創(chuàng)建一個全局的ThreadLocal對象
#每個線程有獨立的存儲空間
#每個線程對ThreadLocal對象都可以讀寫老充,但是互不影響
local = threading.local()
def run(x, n):
x = x + n
x = x - n
def func(n):
#每個線程都有l(wèi)ocal.x葡盗,就是線程的局部變量
local.x = num
for i in range(1000000):
run(local.x, n)
print("%s-%d"%(threading.current_thread().name, local.x))
if __name__ == "__main__":
t1 = threading.Thread(target=func, args=(6,))
t2 = threading.Thread(target=func, args=(9,))
t1.start()
t2.start()
t1.join()
t2.join()
print("num =",num)
#作用:為每個線程綁定一個數(shù)據(jù)庫鏈接,HTTP請求啡浊,用戶身份信息等觅够,這樣一個線程的所有調(diào)用到的處理函數(shù)都可以非常方便地訪問這些資源
信號量控制線程數(shù)量
import threading
num = 0
#創(chuàng)建一個全局的ThreadLocal對象
#每個線程有獨立的存儲空間
#每個線程對ThreadLocal對象都可以讀寫,但是互不影響
local = threading.local()
def run(x, n):
x = x + n
x = x - n
def func(n):
#每個線程都有l(wèi)ocal.x巷嚣,就是線程的局部變量
local.x = num
for i in range(1000000):
run(local.x, n)
print("%s-%d"%(threading.current_thread().name, local.x))
if __name__ == "__main__":
t1 = threading.Thread(target=func, args=(6,))
t2 = threading.Thread(target=func, args=(9,))
t1.start()
t2.start()
t1.join()
t2.join()
print("num =",num)
#作用:為每個線程綁定一個數(shù)據(jù)庫鏈接喘先,HTTP請求,用戶身份信息等廷粒,這樣一個線程的所有調(diào)用到的處理函數(shù)都可以非常方便地訪問這些資源
湊夠一定數(shù)量才能一起執(zhí)行
import threading, time
bar = threading.Barrier(3)
def run():
print("%s--start"%(threading.current_thread().name))
time.sleep(1)
bar.wait()
print("%s--end" % (threading.current_thread().name))
if __name__ == "__main__":
for i in range(5):
threading.Thread(target=run).start()
定時線程
import threading
def run():
print("sunck is a good man")
#延時執(zhí)行線程
t = threading.Timer(5, run)
t.start()
t.join()
print("父線程結(jié)束")
線程通信
import threading, time
def func():
#事件對象
event = threading.Event()
def run():
for i in range(5):
#阻塞窘拯,等待事件的觸發(fā)
event.wait()
#重置
event.clear()
print("sunck is a good man!!%d"%i)
t = threading.Thread(target=run).start()
return event
e = func()
#觸發(fā)事件
for i in range(5):
time.sleep(2)
e.set()
生產(chǎn)者與消費者
import threading,queue,time,random
#生產(chǎn)者
def product(id, q):
while True:
num = random.randint(0, 10000)
q.put(num)
print("生產(chǎn)者%d生產(chǎn)了%d數(shù)據(jù)放入了隊列" % (id, num))
time.sleep(3)
#任務(wù)完成
q.task_done()
#消費者
def customer(id, q):
while True:
item = q.get()
if item is None:
break
print("消費者%d消費了%d數(shù)據(jù)" % (id, item))
time.sleep(2)
# 任務(wù)完成
q.task_done()
if __name__ == "__main__":
# 消息隊列
q = queue.Queue()
# 啟動生產(chǎn)者
for i in range(4):
threading.Thread(target=product, args=(i,q)).start()
# 啟動消費者
for i in range(3):
threading.Thread(target=customer, args=(i,q)).start()
線程調(diào)度
import threading,time
#線程條件變量
cond = threading.Condition()
def run1():
with cond:
for i in range(0, 10, 2):
print(threading.current_thread().name, i)
#time.sleep(1)
cond.wait()
cond.notify()
def run2():
with cond:
for i in range(1, 10, 2):
print(threading.current_thread().name, i)
#time.sleep(1)
cond.notify()
cond.wait()
threading.Thread(target=run1).start()
threading.Thread(target=run2).start()
QQ練習
-
server端
import tkinter import socket import threading win = tkinter.Tk() win.title("QQ服務(wù)器") win.geometry("400x400+200+20") users = {} def run(ck, ca): print("***********") userName = ck.recv(1024) users[userName.decode("utf-8")] = ck print(users) while True: rData = ck.recv(1024) dataStr = rData.decode("utf-8") #xym:sunck is a goodman infolist = dataStr.split(":") users[infolist[0]].send((userName.decode("utf-8") + "說+"+infolist[1]).encode("utf-8")) def start(): ipStr = eip.get() portStr = eport.get() server = socket.socket(socket.AF_INET, socket.SOCK_STREAM) server.bind((ipStr, int(portStr))) server.listen(10) printStr = "服務(wù)器啟動成功" text.insert(tkinter.INSERT, printStr) while True: ck, ca = server.accept() t = threading.Thread(target=run, args=(ck,ca)) t.start() def startServer(): s = threading.Thread(target=start) s.start() lableIp = tkinter.Label(win,text="ip").grid(row=0,column=0) lablePort = tkinter.Label(win,text="port").grid(row=1,column=0) eip = tkinter.Variable() eport = tkinter.Variable() entryIp = tkinter.Entry(win, textvariable=eip).grid(row=0,column=1) entryPort = tkinter.Entry(win, textvariable=eport).grid(row=1,column=1) button = tkinter.Button(win, text="啟動", command=startServer).grid(row=2,column=0) text = tkinter.Text(win, width=30, height=10) text.grid(row=3,column=0) win.mainloop()
-
client端
1、client1.pyimport tkinter import socket import threading win = tkinter.Tk() win.title("sunck") win.geometry("400x400+200+20") ck = None def getInfo(): while True: data = ck.recv(1024) text.insert(tkinter.INSERT, data.decode("utf-8")) def connectServer(): global ck ipStr = eip.get() portStr = eport.get() userStr = euser.get() client = socket.socket(socket.AF_INET, socket.SOCK_STREAM) client.connect((ipStr, int(portStr))) client.send(userStr.encode("utf-8")) ck = client #等待接收數(shù)據(jù) t = threading.Thread(target=getInfo) t.start() def sendMail(): frient = efriend.get() sendStr = esend.get() sendStr = frient + ":" + sendStr ck.send(sendStr.encode("utf-8")) lableUser = tkinter.Label(win,text="userName").grid(row=0,column=0) euser = tkinter.Variable() entryUser = tkinter.Entry(win, textvariable=euser).grid(row=0,column=1) lableIp = tkinter.Label(win,text="ip").grid(row=1,column=0) eip = tkinter.Variable() entryIp = tkinter.Entry(win, textvariable=eip).grid(row=1,column=1) lablePort = tkinter.Label(win,text="port").grid(row=2,column=0) eport = tkinter.Variable() entryPort = tkinter.Entry(win, textvariable=eport).grid(row=2,column=1) button = tkinter.Button(win, text="連接", command=connectServer).grid(row=3,column=0) text = tkinter.Text(win, width=30, height=5) text.grid(row=4,column=0) esend = tkinter.Variable() entrySend = tkinter.Entry(win, textvariable=esend).grid(row=5,column=0) efriend= tkinter.Variable() entryFriend = tkinter.Entry(win, textvariable=efriend).grid(row=6,column=0) button2 = tkinter.Button(win, text="發(fā)送", command=sendMail).grid(row=6,column=1) win.mainloop()
2评雌、client2.py
import tkinter import socket import threading win = tkinter.Tk() win.title("sunck") win.geometry("400x400+200+20") ck = None def getInfo(): while True: data = ck.recv(1024) text.insert(tkinter.INSERT, data.decode("utf-8")) def connectServer(): global ck ipStr = eip.get() portStr = eport.get() userStr = euser.get() client = socket.socket(socket.AF_INET, socket.SOCK_STREAM) client.connect((ipStr, int(portStr))) client.send(userStr.encode("utf-8")) ck = client #等待接收數(shù)據(jù) t = threading.Thread(target=getInfo) t.start() def sendMail(): frient = efriend.get() sendStr = esend.get() sendStr = frient + ":" + sendStr ck.send(sendStr.encode("utf-8")) lableUser = tkinter.Label(win,text="userName").grid(row=0,column=0) euser = tkinter.Variable() entryUser = tkinter.Entry(win, textvariable=euser).grid(row=0,column=1) lableIp = tkinter.Label(win,text="ip").grid(row=1,column=0) eip = tkinter.Variable() entryIp = tkinter.Entry(win, textvariable=eip).grid(row=1,column=1) lablePort = tkinter.Label(win,text="port").grid(row=2,column=0) eport = tkinter.Variable() entryPort = tkinter.Entry(win, textvariable=eport).grid(row=2,column=1) button = tkinter.Button(win, text="連接", command=connectServer).grid(row=3,column=0) text = tkinter.Text(win, width=30, height=5) text.grid(row=4,column=0) esend = tkinter.Variable() entrySend = tkinter.Entry(win, textvariable=esend).grid(row=5,column=0) efriend= tkinter.Variable() entryFriend = tkinter.Entry(win, textvariable=efriend).grid(row=6,column=0) button2 = tkinter.Button(win, text="發(fā)送", command=sendMail).grid(row=6,column=1) win.mainloop()
3树枫、client3.py
import tkinter import socket import threading win = tkinter.Tk() win.title("sunck") win.geometry("400x400+200+20") ck = None def getInfo(): while True: data = ck.recv(1024) text.insert(tkinter.INSERT, data.decode("utf-8")) def connectServer(): global ck ipStr = eip.get() portStr = eport.get() userStr = euser.get() client = socket.socket(socket.AF_INET, socket.SOCK_STREAM) client.connect((ipStr, int(portStr))) client.send(userStr.encode("utf-8")) ck = client #等待接收數(shù)據(jù) t = threading.Thread(target=getInfo) t.start() def sendMail(): frient = efriend.get() sendStr = esend.get() sendStr = frient + ":" + sendStr ck.send(sendStr.encode("utf-8")) lableUser = tkinter.Label(win,text="userName").grid(row=0,column=0) euser = tkinter.Variable() entryUser = tkinter.Entry(win, textvariable=euser).grid(row=0,column=1) lableIp = tkinter.Label(win,text="ip").grid(row=1,column=0) eip = tkinter.Variable() entryIp = tkinter.Entry(win, textvariable=eip).grid(row=1,column=1) lablePort = tkinter.Label(win,text="port").grid(row=2,column=0) eport = tkinter.Variable() entryPort = tkinter.Entry(win, textvariable=eport).grid(row=2,column=1) button = tkinter.Button(win, text="連接", command=connectServer).grid(row=3,column=0) text = tkinter.Text(win, width=30, height=5) text.grid(row=4,column=0) esend = tkinter.Variable() entrySend = tkinter.Entry(win, textvariable=esend).grid(row=5,column=0) efriend= tkinter.Variable() entryFriend = tkinter.Entry(win, textvariable=efriend).grid(row=6,column=0) button2 = tkinter.Button(win, text="發(fā)送", command=sendMail).grid(row=6,column=1) win.mainloop()