操作系統(tǒng)分時技術(shù)(對于進(jìn)程而言):
將處理機(jī)的運行時間分成很短的時間片,按時間片輪流把處理機(jī)分配給各聯(lián)機(jī)作業(yè)使用萤悴。
特點:
多路性璧疗,交互性,獨立性秧倾,及時性
目標(biāo):
對用戶響應(yīng)的及時性怨酝,即不至于用戶等待每一個命令的處理時間過長
進(jìn)程的定義:
執(zhí)行中的程序稱為進(jìn)程;
程序和進(jìn)程的區(qū)別:
程序是指令的集合那先,是進(jìn)程運行的靜態(tài)描述文本农猬;進(jìn)程是程序的一次執(zhí)行活動,屬于動態(tài)概念售淡。
進(jìn)程的缺點:
進(jìn)程只能在一個時間干一件事斤葱;
進(jìn)程在執(zhí)行過程中如果阻塞,例如等待輸入揖闸,整個進(jìn)程就會掛起揍堕,即使進(jìn)程中有些工作不依賴輸入的數(shù)據(jù),也將無法執(zhí)行
例子:若QQ是一個只能在同一時間干一件事的獨立進(jìn)程的話汤纸,則監(jiān)聽鍵盤輸入衩茸,監(jiān)聽他人發(fā)送信息,顯示他人發(fā)送的信息將無法同步實現(xiàn)
線程的定義(thread):
線程是操作系統(tǒng)能夠進(jìn)行運算調(diào)度的最小單位贮泞。它被包含在進(jìn)程中楞慈,是進(jìn)程中的實際運作單位。一條進(jìn)程指的是進(jìn)程中一個單一順序的控制流啃擦,一個進(jìn)程中可以并發(fā)多個線程囊蓝,每條線程并行執(zhí)行不同的任務(wù)
進(jìn)程和線程的區(qū)別:
1.線程是共享進(jìn)程的地址空間來創(chuàng)建的,進(jìn)程有自己的地址空間令蛉。
2.線程可以直接接受它所屬進(jìn)程的數(shù)據(jù)段聚霜,而進(jìn)程需要復(fù)制它父進(jìn)程的數(shù)據(jù)段不能直接調(diào)用。
3.線程可以在所屬進(jìn)程中直接與其它線程進(jìn)行通訊,進(jìn)程必須使用進(jìn)程間通信來與其它進(jìn)程進(jìn)行通訊俯萎。
4.新的線程很容易創(chuàng)建,新的進(jìn)程需要復(fù)制其父進(jìn)程运杭。
5.對于相同進(jìn)程而言夫啊,線程可以遠(yuǎn)超過其它線程對其進(jìn)行控制;進(jìn)程的控制力只有父進(jìn)程大于子進(jìn)程辆憔。
6.修改主線程(取消撇眯,修改優(yōu)先級等)可能會影響進(jìn)程中其它線程;修改父進(jìn)程不能影響子進(jìn)程虱咧。
線程的并發(fā)機(jī)制:
線程有開始熊榛,順序執(zhí)行和結(jié)束三個部分。還有一個指令指針用于記錄運行的位置腕巡,
線程運行被搶占(中斷)玄坦,被暫時的掛起(睡眠),讓其它線程運行(讓步)绘沉。線程在進(jìn)程中共享數(shù)據(jù)煎楣。
線程的并發(fā)本質(zhì):
單cpu中,并行的實現(xiàn)是通過規(guī)定線程運行時間片并進(jìn)行讓步來實現(xiàn)的车伞。
線程并發(fā)的風(fēng)險與解決:
競態(tài)條件(race condition)的產(chǎn)生(數(shù)據(jù)訪問的順序不一樣)择懂,導(dǎo)致數(shù)據(jù)結(jié)果的不一致。
使用線程庫的同步語句來控制線程的執(zhí)行和數(shù)據(jù)的訪問另玖。
python中多線程的實現(xiàn):
方法1直接創(chuàng)建線程對象
import time
from datatime import datatime
import threading #python中提供線程相關(guān)操作的庫
# 每個進(jìn)程默認(rèn)都有一個線程困曙,這個線程叫主線程;其他的線程都叫子線程
def download(file_name): #模擬線程
print("開始下載%s"%file_name,datatime.now()) # 打印當(dāng)前時間谦去,線程起始時間
time.sleep(5) #線程掛起5秒
print("%s下載完成"%file_name,datatime.now()) # 打印當(dāng)前時間慷丽,線程終止時間
# 用模擬單線程進(jìn)行對比
# 1.單線程下載兩個電影:
# 在一個線程中下載兩個電影:時間是兩個電影下載的總和
download("復(fù)聯(lián)4")
download("007")
# 2.線程模塊
# 1)current_thread 函數(shù) - 獲取當(dāng)前線程
print(threading.current_thread()) #當(dāng)前打印的是主進(jìn)程
# 2)Thread類
'''
Thread類的對象就是線程。所以需要子線程就創(chuàng)建這個類的對象
Thread(target,args,kwargs)
target - 函數(shù)鳄哭,需要在當(dāng)前創(chuàng)建的子線程中去調(diào)用的函數(shù)
args/kwargs - 調(diào)用target中的函數(shù)需要的實參列表
'''
# a.創(chuàng)建線程對象
t1 = threading.Thread(target=download, args("復(fù)聯(lián)4",)) #元組寫法
# t1 = threading.Thread(target=download, kwargs = {"film_name":"復(fù)聯(lián)4"})
t2 = threading.Thread(target=download, args("007",))
# b.開始執(zhí)行子線程中的任務(wù):線程對象.start()
'''
通過start方法盈魁,在子線程中去調(diào)用target對應(yīng)的函數(shù)
'''
t1.start()
t2.start()
方法2.創(chuàng)建自己的線程類
from threading import *
import time
from datatime import datatime
# 1.創(chuàng)建自己的線程類
'''
1) 聲明一個類繼承Thread
2) 實現(xiàn)run方法,這個方法中的任務(wù)就是需要在子線程中執(zhí)行的任務(wù)
注意:一個進(jìn)程中如果有多個線程窃诉,程序會在所有的線程都結(jié)束的時候才結(jié)束杨耙;發(fā)生異常崩潰其實崩潰的是線程
'''
class DownloadThread(Thread):
def __init__(self, file_name):
super().__init__()
self.film_name = film_name
def run(self):
# print(current_thread())
# print("在子線程中執(zhí)行的代碼")
print("開始下載:%s"%self.film_name, datatime.now())
# print([1, 2][3]) #檢驗線程崩潰后其他線程是否正常使用
time.sleep(3)
print("結(jié)束下載%s"%self.film_name, datatime.now())
# 3) 用子類直接創(chuàng)建線程對象
t1 = DownloadThread('復(fù)聯(lián)4')
t2 = DownloadThread('長江7號')
# 4) 通過start去執(zhí)行子線程中的任務(wù)
t1.start()
t2.start()
# 注意:t1.run() # 不能直接調(diào)用run方法,因為這樣調(diào)用不會在子線程中執(zhí)行任務(wù)會在主線程中調(diào)用,詳情看源碼和結(jié)果
# t2.run()
'''
<_MainThread(MainThread, started 360)>
開始下載: 復(fù)聯(lián)4 2019-05-10 11:50:05.386988
結(jié)束下載: 復(fù)聯(lián)4 2019-05-10 11:50:08.387990
<_MainThread(MainThread, started 360)>
開始下載: 長江7號 2019-05-10 11:50:08.387990
結(jié)束下載: 長江7號 2019-05-10 11:50:11.388084
'''
while True:
input('>>>:')# 主線程調(diào)用與子線程互不干擾
線程控制join方法
from threading import Thread
from datetime import datetime
import time
from random import randint
class DownloadThread(Thread):
def __init__(self, name, time):
super().__init__()
self.name = name
self.time = time
def run(self):
print("開始下載:%s"%self.name)
time.sleep(self.time)
print("下載結(jié)束:%s"%self.name)
t1 = DownloadThread("abc", 5)
t2 = DownloadThread("def", 7)
t2.start()
t1.start()
# 1.join
'''
如果希望某個任務(wù)是在某個線程結(jié)束后才執(zhí)行飘痛,那就將這個任務(wù)的代碼放在對應(yīng)線程對象調(diào)用join方法的后面
'''
t1.join()
t2.join()
print("下載完成") #實現(xiàn)需求:兩個完成時間不同的任務(wù)均完成后進(jìn)行打印
'''
需求:一個任務(wù)完成后再執(zhí)行另一個任務(wù)珊膜,join方法解決方案
'''
t1 = DownloadThread('沉默的羔羊', 4)
t2 = DownloadThread('恐怖游輪', 5)
t2.start()
t2.join()
t1.start()
競態(tài)條件時數(shù)據(jù)操作的解決方案(鎖)
問題
1.問題
當(dāng)多個線程同時對一個數(shù)據(jù)進(jìn)行讀寫操作,可能會出現(xiàn)一個線程剛把數(shù)據(jù)讀出來還沒來得及寫進(jìn)去宣脉,另外一個線程進(jìn)行讀操作的數(shù)據(jù)安全問題车柠。
2.解決 - 加鎖
1)保證每個數(shù)據(jù)對應(yīng)一個鎖對象
2)操作數(shù)據(jù)前加鎖,數(shù)據(jù)操作完成后釋放鎖
import time
from threading import Thread, Lock
class Account:
def __init__(self, name, tel, balance=50)
self.name = name
self.tel = tel
self.balance = balance
# 關(guān)聯(lián)一個鎖對象
self.lock = Lock()
def save_money(self, money):
print("開始存錢")
# 加鎖
self.lock.acquire()
value = self.balance
time.sleep(4)
self.balance = value + money
print("存錢成功:%.2f"%self.balance)
# 釋放鎖
self.lock.release()
def draw_money(self, money):
print("開始取錢")
#加鎖
self.lock.acquire()
value = self.balance
if value<money:
print("取錢失敗竹祷!余額不足")
return
self.balance = value - money
print("取錢成功:%.2f"%self.balance)
# 釋放鎖
self.lock.release()
account = Account("小H", "13893177183", 10000)
# 一個線程去存錢
t1 = Thread(target=account, save_money, args=(1000,))
# 一個線程去取錢
t2 = Thread(target=account, draw_money, args=(500,))
t1.start()
t2.start()
print("======分隔符======")
# 添加數(shù)據(jù)
share_data = 10000
lock = Lock()
def add_data(value):
lock.acquire()
global share_data #保證函數(shù)內(nèi)賦值share_data時谈跛,賦值的是全局變量
old_data = share_data
time.sleep(4)
share_data = old_data + value #保證函數(shù)內(nèi)賦值share_data時,賦值的是全局變量如果上方未聲明全局變量塑陵,則函數(shù)內(nèi)會創(chuàng)建同名局部變量從而報錯
lock.release()
t1 = Thread(target=add_data, args=(200,))
t2 = Thread(target=add_data, args=(300,))
t1.start()
t2.start()
t1.join()
t2.join()
print(share_data)