Python建立多線程任務並獲取每個線程返回值


1.進程和線程

    (1)進程是一個執行中的程序。每個進程都擁有自己的地址空間、內存、數據棧以及其他用於跟蹤執行的輔助數據。進程也可以派生新的進程來執行其他任務,不過每個新進程都擁有自己的內存和數據棧,所以只能采用進程間通信(IPC)的方式共享信息。

    (2)線程與進程類似,不過他們是在同一個進程下執行的,並共享相同的上下文。線程一般是以並發方式執行的,但是在單核CPU中真正的並發是不可能的,:每個線程運行一小會兒,然后讓步給其他線(再次排隊等待更多的CPU時間)。但是,多線程訪問同一片數據,由於訪問的順序不同可能導致結構不一致。例如append(0-1)和print會同時有01

PS:內存中可以有許多程序,但是在任意給定時刻只能有一個程序在運行。同理,盡管Python 解釋器中可以運行多個線程,但是在任意給定時刻只有一個線程會被解釋器執行。

2.線程

    線程相關的模塊有thread和threading,其中threading是thread的改進和升級版,且thread模塊有一個致命的缺點,在主線程退出之后,所有其他線程都會在沒有清理的情況下直接退出。threading模塊中加入守護線程概念,如果被指明為守護線程后主線程退出后不會等待守護線程執行完畢才吐出。整個Python 程序(可以解讀為:主線程)將在所有非守護線程退出之后才退出,換句話說,就是沒有剩下存活的非守護線程時。

主線程和子線程分別是什么?舉例?

     而主線程應該做一個好的管理者,負責了解每個單獨的線程需要執行什么,每個派生的線程需要哪些數據或參數,這些線程執行完成后會提供什么結果。這樣,主線程就可以收集每個線程的結果,然后匯總成一個有意義的最終結果。

(1)單線程:

  我需要做兩件事,只能做完一件再做第二件,排好隊

# -*-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()

 返回結果:

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實例,傳給他一個函數)

# -*-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 此處並不會執行線程,而是將任務分發到每個線程,同步線程。等同步完成后再開始執行start方法
        threads[i].start()
    for i in nloops:   # jion()方法等待線程完成
        threads[i].jion()
    print('DONE AT:', ctime())
if __name__ == '__main__':
    main()

運行結果:

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同時進行,當時間較長的子線程loop0完成后,主線程print('DONE AT:', ctime())開始任務

(3)多線程(創建threads實例,傳遞給他一個可調用的類實例):

# -*-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 此處並不會執行線程,而是將任務分發到每個線程,同步線程。等同步完成后再開始執行start方法
        threads[i].start()
    for i in nloops:   # jion()方法等待線程完成
        threads[i].join()
    print('DONE AT:', ctime())
if __name__ == '__main__':
    main()

join函數的原理就是一次檢驗線程池中的線程是否結束,沒有結束就阻塞直到線程結束。如果結束則就跳轉執行下一個線程的join函數。如果不用join(),主線程跑的比子線程快會拿不到結果

(3)通過多線程獲取返回值

 

# -*-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 此處並不會執行線程,而是將任務分發到每個線程,同步線程。等同步完成后再開始執行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()

 

  寫這個腳本的一個目的就是看所有腳本共同調用同一個函數,每個腳本都在這個函數中都有一個相同的變量,那么這個變量會被共用還是每個線程自己各有一個閉包。不過函數內定義的變量是閉包,調用函數時創建,返回時銷毀。

最終返回結果如下:

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)如果多個線程共用一個公共數據,那么我們需要做的就是將這個公共數據設置成隊列格式,要不然多個線程共同訪問這個數據可能會出錯,需要加鎖。設置成隊列比加鎖再放鎖效率高多了

(2)線程之間同一個變量id都不一樣,還是不知道是否其他線程會涉足另一個線程

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM