|
1.進程和線程
(1)進程是一個執(zhí)行中的程序。每個進程都擁有自己的地址空間、內(nèi)存、數(shù)據(jù)棧以及其他用于跟蹤執(zhí)行的輔助數(shù)據(jù)。進程也可以派生新的進程來執(zhí)行其他任務(wù),不過每個新進程都擁有自己的內(nèi)存和數(shù)據(jù)棧,所以只能采用進程間通信(IPC)的方式共享信息。
(2)線程與進程類似,不過他們是在同一個進程下執(zhí)行的,并共享相同的上下文。線程一般是以并發(fā)方式執(zhí)行的,但是在單核CPU中真正的并發(fā)是不可能的,:每個線程運行一小會兒,然后讓步給其他線(再次排隊等待更多的CPU時間)。但是,多線程訪問同一片數(shù)據(jù),由于訪問的順序不同可能導(dǎo)致結(jié)構(gòu)不一致。例如append(0-1)和print會同時有01
PS:內(nèi)存中可以有許多程序,但是在任意給定時刻只能有一個程序在運行。同理,盡管Python 解釋器中可以運行多個線程,但是在任意給定時刻只有一個線程會被解釋器執(zhí)行。
2.線程
線程相關(guān)的模塊有thread和threading,其中threading是thread的改進和升級版,且thread模塊有一個致命的缺點,在主線程退出之后,所有其他線程都會在沒有清理的情況下直接退出。threading模塊中加入守護線程概念,如果被指明為守護線程后主線程退出后不會等待守護線程執(zhí)行完畢才吐出。整個Python 程序(可以解讀為:主線程)將在所有非守護線程退出之后才退出,換句話說,就是沒有剩下存活的非守護線程時。
主線程和子線程分別是什么?舉例?
而主線程應(yīng)該做一個好的管理者,負責(zé)了解每個單獨的線程需要執(zhí)行什么,每個派生的線程需要哪些數(shù)據(jù)或參數(shù),這些線程執(zhí)行完成后會提供什么結(jié)果。這樣,主線程就可以收集每個線程的結(jié)果,然后匯總成一個有意義的最終結(jié)果。
(1)單線程:
我需要做兩件事,只能做完一件再做第二件,排好隊
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | # -*-coding:utf-8-*-
from time import ctime, sleep
import threading
loops = [4, 2]
def loop(nloop,nsec):
print('start loop', nloop, 'at :', ctime())
sleep(nsec)
print('done loop', nloop, 'at:', ctime())
def main():
print('start at',ctime())
nloops = range(len(loops))
for i in nloops:
loop(i, loops[i])
print('DONE AT:', ctime())
if __name__ == '__main__':
main()
|
返回結(jié)果:
start at Sun Dec 3 12:10:52 2017 start loop 0 at : Sun Dec 3 12:10:52 2017 done loop 0 at: Sun Dec 3 12:10:56 2017 start loop 1 at : Sun Dec 3 12:10:56 2017 done loop 1 at: Sun Dec 3 12:10:58 2017 DONE AT: Sun Dec 3 12:10:58 2017
(2) 多線程:(建立threads實例,傳給他一個函數(shù))
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | # -*-coding:utf-8-*-
from time import ctime, sleep
import threading
loops = [4, 2]
def loop(nloop,nsec):
print('start loop', nloop, 'at :', ctime())
sleep(nsec)
print('done loop', nloop, 'at:', ctime())
def main():
print('start at',ctime())
threads = []
nloops = range(len(loops))
for i in nloops:
t = threading.Thread(target=loop,args=(i,loops[i]))
threads.append(t)
for i in nloops: # start threads 此處并不會執(zhí)行線程,而是將任務(wù)分發(fā)到每個線程,同步線程。等同步完成后再開始執(zhí)行start方法
threads[i].start()
for i in nloops: # jion()方法等待線程完成
threads[i].jion()
print('DONE AT:', ctime())
if __name__ == '__main__':
main()
|
運行結(jié)果:
start at Sun Dec 3 12:08:23 2017 start loop 0 at : Sun Dec 3 12:08:23 2017 start loop 1 at : Sun Dec 3 12:08:23 2017 done loop 1 at: Sun Dec 3 12:08:25 2017 done loop 0 at: Sun Dec 3 12:08:27 2017 DONE AT: Sun Dec 3 12:08:27 2017
可以看到loop0和loop1同時進行,當(dāng)時間較長的子線程loop0完成后,主線程print('DONE AT:', ctime())開始任務(wù)
(3)多線程(創(chuàng)建threads實例,傳遞給他一個可調(diào)用的類實例):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 | # -*-coding:utf-8-*-
from time import ctime, sleep
import threading
loops = [4, 2]
class MyThread(object):
def __init__(self, func, args, name=''):
self.name = name
self.func = func
self.args = args
def __call__(self):
self.func(*self.args)
def loop(nloop, nsec):
print('start loop', nloop, 'at :', ctime())
sleep(nsec)
print('done loop', nloop, 'at:', ctime())
def main():
print('start at',ctime())
threads = []
nloops = range(len(loops))
for i in nloops:
t = threading.Thread(target=MyThread(loop, (i, loops[i]), loop.__name__))
threads.append(t)
for i in nloops: # start threads 此處并不會執(zhí)行線程,而是將任務(wù)分發(fā)到每個線程,同步線程。等同步完成后再開始執(zhí)行start方法
threads[i].start()
for i in nloops: # jion()方法等待線程完成
threads[i].join()
print('DONE AT:', ctime())
if __name__ == '__main__':
main()
|
join函數(shù)的原理就是一次檢驗線程池中的線程是否結(jié)束,沒有結(jié)束就阻塞直到線程結(jié)束。如果結(jié)束則就跳轉(zhuǎn)執(zhí)行下一個線程的join函數(shù)。如果不用join(),主線程跑的比子線程快會拿不到結(jié)果
(3)通過多線程獲取返回值
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 | # -*-coding:utf-8-*-
from time import ctime, sleep
import threading
import numpy as np
import collections
loops = ['廣州', '北京']
t_list = ['01', '02', '03']
cldas_sum = collections.deque()
class MyThread(threading.Thread):
def __init__(self, func, args, name=''):
threading.Thread.__init__(self)
self.name = name
self.func = func
self.args = args
self.result = self.func(*self.args)
def get_result(self):
try:
return self.result
except Exception:
return None
def loop(nloop):
for j in t_list:
cldas_values = []
for k in range(4):
cldas_value = nloop + str(k)
cldas_values.append(cldas_value)
cldas_values.append(j)
cldas_values.append(nloop)
cldas_sum.append(cldas_values)
print(id(cldas_values))
#print(cldas_sum)
return cldas_sum
def main():
print('start at', ctime())
threads = []
nloops = range(len(loops))
for i in nloops:
t = MyThread(loop, (loops[i],), loop.__name__)
threads.append(t)
for i in nloops: # start threads 此處并不會執(zhí)行線程,而是將任務(wù)分發(fā)到每個線程,同步線程。等同步完成后再開始執(zhí)行start方法
threads[i].start()
for i in nloops: # jion()方法等待線程完成
threads[i].join()
print(threads[1].get_result())
print('DONE AT:', ctime())
if __name__ == '__main__':
main()
|
寫這個腳本的一個目的就是看所有腳本共同調(diào)用同一個函數(shù),每個腳本都在這個函數(shù)中都有一個相同的變量,那么這個變量會被共用還是每個線程自己各有一個閉包。不過函數(shù)內(nèi)定義的變量是閉包,調(diào)用函數(shù)時創(chuàng)建,返回時銷毀。
最終返回結(jié)果如下:
start at Tue Dec 5 10:32:38 2017 728072411976 728072411848 728072411784 728072411656 728072364680 728072364808 deque([['廣州0', '廣州1', '廣州2', '廣州3', '01', '廣州'], ['廣州0', '廣州1', '廣州2', '廣州3', '02', '廣州'], ['廣州0', '廣州1', '廣州2', '廣州3', '03', '廣州'], ['北京0', '北京1', '北京2', '北京3', '01', '北京'], ['北京0', '北京1', '北京2', '北京3', '02', '北京'], ['北京0', '北京1', '北京2', '北京3', '03', '北京']]) DONE AT: Tue Dec 5 10:32:38 2017
需要注意的是:
(1)如果多個線程共用一個公共數(shù)據(jù),那么我們需要做的就是將這個公共數(shù)據(jù)設(shè)置成隊列格式,要不然多個線程共同訪問這個數(shù)據(jù)可能會出錯,需要加鎖。設(shè)置成隊列比加鎖再放鎖效率高多了
(2)線程之間同一個變量id都不一樣,還是不知道是否其他線程會涉足另一個線程
|