多進(jìn)程
多任務(wù):生活中來看,就是多個(gè)任務(wù)同時(shí)進(jìn)行,喝酒聊天狐粱,開車舀寓,手腳并用,唱歌跳舞
電腦中:錄屏肌蜻、sublime互墓、vnc服務(wù)端、瀏覽器打開等
代碼中:實(shí)現(xiàn)多任務(wù)蒋搜,多進(jìn)程篡撵、多線程
進(jìn)程:電腦中,每一個(gè)軟件啟動(dòng)起來都是一個(gè)進(jìn)程豆挽,
代碼中:沒有運(yùn)行的時(shí)候稱之為程序育谬,運(yùn)行起來之后就是一個(gè)進(jìn)程
多進(jìn)程:進(jìn)程只有一個(gè),稱之為主進(jìn)程帮哈,子進(jìn)程膛檀,要實(shí)現(xiàn)兩個(gè)函數(shù)同時(shí)執(zhí)行,就要通過主進(jìn)程來創(chuàng)建子進(jìn)程
操作系統(tǒng)實(shí)現(xiàn)娘侍,只是在進(jìn)程之間來回切換咖刃,切換的非常快憾筏,看著像同時(shí)執(zhí)行一樣
如何實(shí)現(xiàn)嚎杨?
面向過程:(process)
p = Process(target=xxx, name=xxx, args=(xxx,))
target: 進(jìn)程啟動(dòng)之后要執(zhí)行的函數(shù)
name: 給進(jìn)程起個(gè)名字
args: 給子進(jìn)程傳遞的參數(shù),是一個(gè)元組
p.start() 啟動(dòng)進(jìn)程
p.join() 讓主進(jìn)程等待子進(jìn)程結(jié)束
os.getpid() 獲取當(dāng)前進(jìn)程id號(hào)
os.getppid() 獲取父進(jìn)程的id號(hào)
from multiprocessing import Process
import time
import os
# 想讓子進(jìn)程1執(zhí)行sing函數(shù)
def sing(a):
print('接受的參數(shù)為%s' % a)
# 進(jìn)程id號(hào)
print('進(jìn)程-%s-開始運(yùn)行' % os.getpid())
print('父進(jìn)程為%s' % os.getppid())
for x in range(1, 5):
print('我在唱小情歌')
time.sleep(1)
# 想讓子進(jìn)程2執(zhí)行dance函數(shù)
def dance(a):
print('進(jìn)程-%s-開始運(yùn)行' % os.getpid())
print('父進(jìn)程為%s' % os.getppid())
for x in range(1, 5):
print('我在跳鋼管舞')
time.sleep(1)
def main():
# 主進(jìn)程
print('主進(jìn)程id號(hào)為%s' % os.getpid())
a = '青花瓷'
# 創(chuàng)建進(jìn)程
p_sing = Process(target=sing, name='唱歌', args=(a,))
p_dance = Process(target=dance, name='跳舞', args=(a,))
# 啟動(dòng)進(jìn)程
p_sing.start()
p_dance.start()
# 獲取進(jìn)程名字
print(p_sing.name)
print(p_dance.name)
# 因?yàn)橹鬟M(jìn)程中有子進(jìn)程的信息氧腰,所以主進(jìn)程必須等子進(jìn)程結(jié)束之后再結(jié)束
p_sing.join()
p_dance.join()
print('主進(jìn)程結(jié)束')
if __name__ == '__main__':
main()
面向?qū)ο螅?/h4>
from multiprocessing import Process
import time
class SingProcess(Process):
def __init__(self, a):
# 如果要重寫構(gòu)造方法枫浙,一定得手動(dòng)調(diào)用父類的構(gòu)造方法
super().__init__()
self.a = a
def run(self):
print('傳遞的參數(shù)為%s' % self.a)
for x in range(1, 5):
print('我在唱小情歌')
time.sleep(1)
class DanceProcess(Process):
def run(self):
for x in range(1, 5):
print('我在跳鋼管舞')
time.sleep(1)
def main():
a = '現(xiàn)在很多老歌手為什么不唱歌了'
p_sing = SingProcess(a)
# 啟動(dòng)進(jìn)程,進(jìn)程啟動(dòng)之后默認(rèn)執(zhí)行類里面的run方法
p_sing.start()
# p_dance = DanceProcess()
# p_dance.start()
p_sing.join()
# p_dance.join()
print('主進(jìn)程結(jié)束')
if __name__ == '__main__':
main()
from multiprocessing import Process
import time
class SingProcess(Process):
def __init__(self, a):
# 如果要重寫構(gòu)造方法枫浙,一定得手動(dòng)調(diào)用父類的構(gòu)造方法
super().__init__()
self.a = a
def run(self):
print('傳遞的參數(shù)為%s' % self.a)
for x in range(1, 5):
print('我在唱小情歌')
time.sleep(1)
class DanceProcess(Process):
def run(self):
for x in range(1, 5):
print('我在跳鋼管舞')
time.sleep(1)
def main():
a = '現(xiàn)在很多老歌手為什么不唱歌了'
p_sing = SingProcess(a)
# 啟動(dòng)進(jìn)程,進(jìn)程啟動(dòng)之后默認(rèn)執(zhí)行類里面的run方法
p_sing.start()
# p_dance = DanceProcess()
# p_dance.start()
p_sing.join()
# p_dance.join()
print('主進(jìn)程結(jié)束')
if __name__ == '__main__':
main()
多進(jìn)程拷貝,拷貝文件夾,假如文件夾里面只有文件十性。假如有100個(gè)文件,那么拷貝的時(shí)候先拷貝第一個(gè)膏潮,然后第二個(gè),然后第三個(gè)====
拷貝一個(gè)文件就是一個(gè)任務(wù)满力,那一共100文件焕参,那難道要開辟100個(gè)進(jìn)程嗎?
進(jìn)程并不是越多越好油额,切換占用的時(shí)間越大
練習(xí):多進(jìn)程拷貝
拷貝之前記錄時(shí)間戳叠纷,拷貝之后記錄時(shí)間戳,計(jì)算拷貝的時(shí)間
多進(jìn)程拷貝也是一樣潦嘶,哪個(gè)時(shí)間短
引入進(jìn)程池涩嚣,規(guī)定能創(chuàng)建幾個(gè)進(jìn)程,來了任務(wù),5個(gè)進(jìn)程
進(jìn)程之間是否共享全局變量
全局變量
進(jìn)程之間是否共享全局變量航厚,不共享
每一個(gè)進(jìn)程都是單獨(dú)的代碼
from multiprocessing import Process
import os
import time
count = 100
# 該進(jìn)程用來修改全局變量的值
def change():
global count
count += 100
print('進(jìn)程%s修改后的值為%s' % (os.getpid(), count))
# 該進(jìn)程用來讀取全局變量的值
def read():
print('進(jìn)程%s讀取的值為%s' % (os.getpid(), count))
def test(c):
a = 100
if c == 'hello':
a += 100
time.sleep(2)
print('進(jìn)程%s修改a的值為%s' % (os.getpid(), a))
else:
time.sleep(5)
print('進(jìn)程%s讀取a的值為%s' % (os.getpid(), a))
def main():
'''
p1 = Process(target=change)
p1.start()
time.sleep(2)
p2 = Process(target=read)
p2.start()
'''
a = 'hello'
b = 'world'
p1 = Process(target=test, args=(a, ))
p2 = Process(target=test, args=(b, ))
p1.start()
p2.start()
p1.join()
p2.join()
if __name__ == '__main__':
main()
進(jìn)程池
from multiprocessing import Process
from multiprocessing import Pool
import os
import time
def test(name):
print('任務(wù)%s正在運(yùn)行,進(jìn)程id號(hào)為%s' % (name, os.getpid()))
time.sleep(2)
def main():
# 創(chuàng)建一個(gè)進(jìn)程池對(duì)象
po = Pool(3)
# 給進(jìn)程池添加任務(wù)
lt = ['關(guān)羽', '趙云', '張飛', '馬超', '黃忠', '呂布', '孫策', '大喬']
for name in lt:
po.apply_async(test, args=(name, ))
# 進(jìn)程池使用完畢之后顷歌,關(guān)閉
po.close()
# 讓主進(jìn)程等待結(jié)束
po.join()
print('主進(jìn)程、進(jìn)程池全部結(jié)束')
if __name__ == '__main__':
main()
多線程
線程:比如qq幔睬。比如暴風(fēng)影音眯漩,比如word
可以同時(shí)語音、同時(shí)視頻麻顶、同時(shí)聊天赦抖,多線程
暴風(fēng)影音,視頻播放辅肾、音頻播放队萤,多線程
word,打字矫钓,拼寫檢查要尔,等,多線程
多任務(wù)的實(shí)現(xiàn):多進(jìn)程份汗、多線程
主進(jìn)程-子進(jìn)程1-子進(jìn)程2
特點(diǎn):進(jìn)程之間沒有關(guān)系盈电,如果一個(gè)進(jìn)程掛了蝴簇,不影響其它子進(jìn)程
進(jìn)程-主線程-子線程1-子線程2
特點(diǎn):線程之間有關(guān)系杯活,如果一個(gè)線程掛了,整個(gè)進(jìn)程就掛了
實(shí)現(xiàn)方式:(thread)
面向過程
t = Thread(target=xxx, name=xxx, args=(xxx,))
target: 線程啟動(dòng)之后要執(zhí)行的函數(shù)
name: 線程的名字
args: 給線程傳遞的參數(shù)
t.start(): 啟動(dòng)線程
t.join(): 讓主線程等待子線程結(jié)束
threading.current_thread().name : 獲取線程名字
import threading
import time
def sing(song):
print('傳遞過來的參數(shù)為%s' % song)
print('獲取線程的名字為%s' % threading.current_thread().name)
for x in range(1, 5):
print('我在唱老情歌')
time.sleep(1)
def dance():
for x in range(1, 5):
print('我在跳廣場(chǎng)舞')
time.sleep(1)
def main():
a = '廣島之戀'
# 這是一個(gè)進(jìn)程熬词,進(jìn)程里面有一個(gè)主線程旁钧,然后主線程創(chuàng)建子線程1(唱歌),子線程2(跳舞)
t_sing = threading.Thread(target=sing, name='唱歌', args=(a, ))
t_dance = threading.Thread(target=dance, name='跳舞')
# 啟動(dòng)線程
t_sing.start()
t_dance.start()
t_sing.join()
t_dance.join()
print('主線程互拾、子線程同時(shí)結(jié)束')
if __name__ == '__main__':
main()
面向?qū)ο?/h3>
import threading
import time
# 滕王閣序 王勃
# 命硬
# 沁園春-雪
# 出師表
class MyThread(threading.Thread):
def __init__(self, a):
super().__init__()
self.a = a
def run(self):
print('傳遞過來的參數(shù)為%s' % self.a)
for x in range(1, 5):
print('鳳凰臺(tái)上鳳凰游,鳳去臺(tái)空江自流')
time.sleep(1)
def main():
a = '落霞與孤鶩齊飛歪今,秋水共長(zhǎng)天一色'
t = MyThread(a)
t.start()
t.join()
print('主線程、子線程全部結(jié)束')
if __name__ == '__main__':
main()
是否共享
import threading
import time
# 滕王閣序 王勃
# 命硬
# 沁園春-雪
# 出師表
class MyThread(threading.Thread):
def __init__(self, a):
super().__init__()
self.a = a
def run(self):
print('傳遞過來的參數(shù)為%s' % self.a)
for x in range(1, 5):
print('鳳凰臺(tái)上鳳凰游,鳳去臺(tái)空江自流')
time.sleep(1)
def main():
a = '落霞與孤鶩齊飛歪今,秋水共長(zhǎng)天一色'
t = MyThread(a)
t.start()
t.join()
print('主線程、子線程全部結(jié)束')
if __name__ == '__main__':
main()
全局變量
共享全局變量
局部變量
不共享局部變量
線程安全問題
線程之間可以共享全局變量
import threading
import os
import time
count = 100
# 該線程用來修改全局變量的值
def change():
global count
count += 100
print('線程%s修改后的值為%s' % (threading.current_thread().name, count))
# 該線程用來讀取全局變量的值
def read():
print('線程%s讀取的值為%s' % (threading.current_thread().name, count))
def test():
a = 100
name = threading.current_thread().name
if name == 'lala':
a += 100
time.sleep(2)
print('線程%s修改a的值為%s' % (name, a))
else:
time.sleep(5)
print('線程讀取a的值為%s' % a)
def main():
'''
t1 = threading.Thread(target=change, name='修改thread')
t1.start()
time.sleep(2)
t2 = threading.Thread(target=read, name='讀取thread')
t2.start()
'''
t1 = threading.Thread(target=test, name='lala')
t2 = threading.Thread(target=test)
t1.start()
t2.start()
t1.join()
t2.join()
if __name__ == '__main__':
main()
加鎖
加鎖
lock.acquire()
釋放鎖
lock.release()
import threading
import time
count = 100
# 創(chuàng)建一把鎖
lock = threading.Lock()
def test(number):
start = time.time()
# 在這里面修改count的值
global count
for x in range(1, 10000000):
# 加鎖
lock.acquire()
count += number
count -= number
# 釋放鎖
lock.release()
end = time.time()
# 上廁所颜矿,大號(hào)寄猩,如何解決,加鎖
# 用之前骑疆,加鎖田篇,用完之后,釋放鎖
# 犧牲了效率了
print('循環(huán)計(jì)算的時(shí)間為%s' % (start - end))
def main():
t1 = threading.Thread(target=test, args=(3, ))
t2 = threading.Thread(target=test, args=(5, ))
t2.start()
t1.start()
t1.join()
t2.join()
print('主線程中打印的count的值為%s' % count)
if __name__ == '__main__':
main()
隊(duì)列
隊(duì)列:買火車票箍铭,電動(dòng)三輪泊柬,特點(diǎn):先進(jìn)先出
用在哪?
線程之間使用隊(duì)列進(jìn)行交互诈火,讓線程解耦合兽赁,生產(chǎn)者-消費(fèi)者模型
線程1-生產(chǎn)數(shù)據(jù)
交互渠道:隊(duì)列
線程2-消費(fèi)數(shù)據(jù)
while 1:
生產(chǎn)數(shù)據(jù)
消費(fèi)數(shù)據(jù)
隊(duì)列使用:
from queue import Queue
q = Queue(5)
q.put() 添加元素
q.put(False) 如果隊(duì)列已滿,添加元素立即拋出異常
q.put(True, 5) 如果隊(duì)列已滿,添加元素5s之后拋出異常
q.get() 獲取元素
q.get(False) 如果隊(duì)列為空刀崖,獲取元素立即拋出異常
q.get(True, 5) 如果隊(duì)列為空惊科,獲取元素5s之后拋出異常
q.empty() 隊(duì)列是否為空
q.full() 隊(duì)列是否已滿
q.qsize() 隊(duì)列長(zhǎng)度
from queue import Queue
# 創(chuàng)建一個(gè)隊(duì)列
# 如果寫,代表隊(duì)列的長(zhǎng)度亮钦,如果不寫译断,隊(duì)列長(zhǎng)度無限
q = Queue(5)
print(q.empty())
print(q.full())
print(q.qsize())
# 向隊(duì)列中添加數(shù)據(jù)
q.put('吳彥祖')
q.put('岳云鵬')
q.put('王寶強(qiáng)')
q.put('黃渤')
q.put('劉德華')
print(q.empty())
print(q.full())
print(q.qsize())
# q.put('古天樂', True, 5)
# 從隊(duì)列中獲取數(shù)據(jù)
print(q.get())
print(q.get())
print(q.get())
print(q.get())
print(q.get())
# print(q.get(True, 5))
多線程爬蟲
分析:
爬蟲里面如何分多線程,
循環(huán):
拼接url或悲,發(fā)送請(qǐng)求孙咪,獲取響應(yīng)
解析響應(yīng),保存到文件
涉及到:
采集線程巡语,3個(gè)線程同時(shí)采集
解析線程翎蹈,3個(gè)線程同時(shí)解析
頁碼隊(duì)列:里面是要爬取的頁碼數(shù)據(jù)
數(shù)據(jù)隊(duì)列:采集線程向隊(duì)列中添加數(shù)據(jù)
解析線程從隊(duì)列中獲取數(shù)據(jù)
保存到同一個(gè)文件中,鎖機(jī)制
import threading
from queue import Queue
import time
from lxml import etree
import requests
import json
class CrawlThread(threading.Thread):
def __init__(self, name, page_queue, data_queue):
super().__init__()
self.name = name
# 保存頁碼隊(duì)列
self.page_queue = page_queue
self.data_queue = data_queue
# url
self.url = 'http://www.fanjian.net/duanzi-{}'
self.headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.99 Safari/537.36',
}
def run(self):
print('%s線程開始啟動(dòng)' % self.name)
# 這里面的思路是什么男公?
while 1:
if self.page_queue.empty():
break
# 1荤堪、從頁碼隊(duì)列中獲取頁碼
page = self.page_queue.get()
# 2、將url和頁碼進(jìn)行拼接
url = self.url.format(page)
# 3枢赔、發(fā)送請(qǐng)求澄阳,獲取響應(yīng)
r = requests.get(url=url, headers=self.headers)
time.sleep(1)
# 4、將響應(yīng)內(nèi)容放入到數(shù)據(jù)隊(duì)列中
self.data_queue.put(r.text)
print('%s線程結(jié)束' % self.name)
class ParseThread(threading.Thread):
def __init__(self, name, data_queue, lock, fp):
super().__init__()
self.name = name
# 保存數(shù)據(jù)隊(duì)列
self.data_queue = data_queue
self.lock = lock
self.fp = fp
def run(self):
print('%s線程開始啟動(dòng)' % self.name)
# 解析線程解析步驟
while 1:
if self.data_queue.empty():
break
# 1踏拜、從數(shù)據(jù)隊(duì)列中取出一個(gè)數(shù)據(jù)
content = self.data_queue.get()
# 2碎赢、解析這個(gè)數(shù)據(jù)
items = self.parse_content(content)
# 3、寫入到文件中
string = json.dumps(items, ensure_ascii=False)
# 加鎖
self.lock.acquire()
self.fp.write(string)
# 釋放鎖
self.lock.release()
print('%s線程結(jié)束' % self.name)
# 解析數(shù)據(jù)函數(shù)
def parse_content(content):
# 生成tree對(duì)象
tree = etree.HTML(content)
# 先找到所有的li標(biāo)簽
li_list = tree.xpath('//li[@class="cont-item"]')
items = []
for oli in li_list:
# 獲取頭像
face = oli.xpath('.//div[@class="cont-list-reward"]//img/@data-src')[0]
# 獲取名字
name = oli.xpath('.//div[@class="cont-list-head"]/a/text()')[0]
# 獲取內(nèi)容
text = oli.xpath('.//div[@class="cont-list-main"]/p/text()')[0]
# 獲取時(shí)間
shijian = oli.xpath('.//div[@class="cont-list-info fc-gray"]/text()')[-1]
item = {
'頭像': face,
'名字': name,
'內(nèi)容': text,
'時(shí)間': shijian,
}
# 將字典添加到列表中
items.append(item)
return items
def create_queue():
page_queue = Queue()
data_queue = Queue()
# 向頁碼隊(duì)列中添加頁碼
for page in range(1, 11):
page_queue.put(page)
return page_queue, data_queue
def main():
# 做什么速梗?
# 創(chuàng)建鎖
lock = threading.Lock()
# 打開文件
fp = open('duanzi.txt', 'w', encoding='utf8')
# 創(chuàng)建兩個(gè)隊(duì)列
page_queue, data_queue = create_queue()
# 創(chuàng)建采集肮塞、解析線程
crawlname_list = ['采集線程1', '采集線程2', '采集線程3']
parsename_list = ['解析線程1', '解析線程2', '解析線程3']
# 列表,用來保存所有的采集線程和解析線程
t_crawl_list = []
t_parse_list = []
for crawlname in crawlname_list:
t_crawl = CrawlThread(crawlname, page_queue, data_queue)
t_crawl.start()
# 將對(duì)應(yīng)的采集線程保存起來
t_crawl_list.append(t_crawl)
for parsename in parsename_list:
t_parse = ParseThread(parsename, data_queue, lock, fp)
# 將對(duì)應(yīng)的解析線程保存起來
t_parse_list.append(t_parse)
t_parse.start()
# 讓主線程等待子線程結(jié)束之后再結(jié)束
for t_crawl in t_crawl_list:
t_crawl.join()
for t_parse in t_parse_list:
t_parse.join()
fp.close()
print('主線程姻锁、子線程全部結(jié)束')
if __name__ == '__main__':
main()
# 留給大家了枕赵,為什么里面沒有寫數(shù)據(jù)呢?