day9-继承式多线程

一、前言

  之前,我们只是简单的介绍了多线程演示,也通过时间设置看出来了,多线程和单线程的不同。现在我们进行更深入的了解,来聊一聊,另外一种多线程方式,继承式多线程,和一个多线程的等待。

二、继承式多线程

2.1、定义

说明:继承式多线程是自己自定义类,去继承theading.Tread这个类,通过启动,去执行run方法中的代码。

import threading
import time

class MyThead(threading.Thread):   #继承threading.Thread
    "继承式多线程"
    def __init__(self,n):
        super(MyThead,self).__init__()
        self.n = n

    def run(self):     #重写run方法
        "这个方法不能叫别的名字,只能叫run方法"
        print("runinit task",self.n)
        time.sleep(2)

t1 = MyThead("t1")   #实例化
t2 = MyThead("t2")

t1.start()   #启动一个多线程
t2.start()

三、启动多个线程

  我们之前只启动了一个2个线程,还是用那种古老的方式t1,t2。那我想一下子起10个或者100个线程,该如何起呐?这边我们可以启动线程的时候,把它加到循环里面去。并且来计算一下它的时间。

3.1、启动多个线程

说明:我们这边为了方便实验,就启动5个线程吧,暂时不启动那么多,并且计算一下时间。那有些同学说了,你为啥不启动1000个线程或者更多一点的线程。这边注意了:你的计算机是4核的,它能干的事情,就是4个任务。你启动的线程越多,就代表着要在这个很多线程之间进行上下文切换。相当于我有一本书,我只看了半页,因为cpu要确保每个人都能执行。也就是说我一本说我要确保你们每一位同学都能看到,那就相当于每个人执行的时间非常少。我把这本说拿过来,一下子又被第二个人,第三个人拿走了。所以就导致所有的人都慢了,所以说你的线程启动1000,启动10000就没有意义了,导致机器越来越慢,所以要适当设置。下面我们就来看看一下子启动多个线程的例子,例子如下:

import threading,time

def run(n):   #这边的run方法的名字是自行定义的,跟继承式多线程不一样,那个是强制的
    print("task:",n)
    time.sleep(2)
    print("task done",n)

start_time = time.time()  #开始时间
for i in range(5):   #一次性启动5个线程
    t = threading.Thread(target=run,args=("t-{0}".format(i),))
    t.start()

print("--------all thead has finished")
print("cost:",time.time()-start_time)  #计算总耗时

#输出
task: t-0
task: t-1
task: t-2
task: t-3
task: t-4
--------all thead has finished
cost: 0.00096893310546875
task done t-1
task done t-2
task done t-0
task done t-4
task done t-3

  大家有没有从上面的程序发现问题,就是我主线程没有等其他的子线程执行完毕,就直接往下执行了,这是为什么呢?而且这个计算的时间根本不是我们想要的时间,中间的2秒哪里去了?

答案:一个程序至少有一个线程,那先往下走的,没有等的就是主线程,主线程启动了子线程之后,子线程就是独立的,跟主线程就没有关系了。主线程和它启动的子线程是并行关系,这就解释了为什么我的主线程启动子线程之后,没有等子线程,而继续往下走了。所以我们计算不出来时间,因为程序已经不是串行的了。程序本身就是一个线程,就是主线程。但是我就是想测试总共花了多长时间,咋办呐?不要着急精彩继续

四、等待线程执行结果

4.1、join设置等待线程执行结果

说明:通过设置在主线程里去等待子线程的执行结果,有了这个执行结果就完了,我要拿所有子线程的执行结果,如果有子线程的执行结果,我们一切都好办了。

import threading
import time

class MyThead(threading.Thread):
    "继承式多线程"
    def __init__(self,n):
        super(MyThead,self).__init__()
        self.n = n

    def run(self):
        "这个方法不能叫别的名字,只能叫run方法"
        print("runinit task",self.n)
        time.sleep(2)

t1 = MyThead("t1")
t2 = MyThead("t2")

t1.start()
t1.join()   #等待t1线程的执行结果,相当于于其他语言里面的 t1.wait()
t2.start()

注:

  1. t1.join() => 等待第一个线程的执行结果,这个结果在没有返回之前,程序是不往下走的。所以你这个这个程序什么吗?这个程序变成串行的了。
  2. t2.start() => 这个后面没有写 join() 这个方法,但是程序在退出之前,它肯定要确保线程都执行完毕,所以它就默认就有一个join。所以最后不写。

4.2、实现并发效果

说明:上面虽然有我想要的结果,却失去了并行的效果。我想要的是线程依然是并行效果,但是只不过,所有的线程统一之后再等主线程往下走。

import threading,time

class MyThead(threading.Thread):
    "继承式多线程"
    def __init__(self,n,sleep_time): #增加时间属性
        super(MyThead,self).__init__()
        self.n = n
        self.sleep_time = sleep_time

    def run(self):
        print("runinit task",self.n)
        time.sleep(self.sleep_time)   #每个线程可以传入不不同的时间
        print("task done,",self.n)

t1 = MyThead("t1",2)   #t1传入2秒
t2 = MyThead("t2",4)   #t2传入4秒

t1.start()
t2.start()

t1.join() #把t1.join()放在线程启动之后
print("main thead.....")

#执行结果
runinit task t1
runinit task t2
task done, t1
main thead.....
task done, t2

注意了:t1.join() => 这边只等t1的结果,然后主线程继续往下走,因为t2需要等4秒,所以,最后打出来的是t2的执行结果。t1的结果到了,就立刻算结果。这边只计算了t1的结果,没有t2的结果。如果想算t2的结果,就 t2.join()。上面代码不理解如图:

4.3、计算多个线程的执行时间

说明:我们用上面的执行重新改进一下第二知识点里面的代码,来计算一下10个线程启动执行的时间。

import threading,time

def run(n):
    print("task:",n)
    time.sleep(2)
    print("task done",n)

start_time = time.time()
t_objs = [] #存放子线程实例
for i in range(5):
    t = threading.Thread(target=run,args=("t-{0}".format(i),))
    t.start()
    t_objs.append(t)   #为了不阻塞后面线程的启动,不在这里join,先放到一个列表中

for t in t_objs:  #循环线程实例列表,等待所有线程执行完毕
    t.join()   

print("--------all thead has finished")
print("cost:",time.time()-start_time)

#输出
task: t-0
task: t-1
task: t-2
task: t-3
task: t-4
task done t-2
task done t-1
task done t-0
task done t-4
task done t-3
--------all thead has finished
cost: 2.003025770187378

哈哈,这样结果正是我们想要的。

posted @ 2017-07-29 12:50  帅丶高高  阅读(168)  评论(0编辑  收藏  举报