python的进程与线程(二)

线程

        之前了解了操作系统的发展史,也知道了进程和线程的概念,归纳一下就是:

        进程:本质上就是一段程序的运行过程(抽象的概念)

        线程:最小的执行单元,是进程的实体

        进程:最小的资源单位
 

线程的调用

        在python中,一般通过导入threading模块来调用线程。 threading 模块建立在thread 模块之上。thread模块以低级、原始的方式来处理和控制线程,而threading 模块通过对thread进行二次封装,提供了更方便的api来处理线程,先看一段直接调用的代码:

       直接调用:     

# -*- coding: utf-8 -*-
import threading,time

def func1(n):
    print('传给我的是%s'%n)
    time.sleep(3)

def func2():
    print('我没有参数的')
    time.sleep(5)

if __name__ == '__main__':
    t1 = threading.Thread(target=func1,args=(5,))   #target传入的是函数名,不带括号,args要以元组形式传入参数
    t2 = threading.Thread(target=func2)   #没有参数就不用传

    t1.start()    #运行线程,本质是调用run方法
    t2.start()
    print('ending......')
    
>>>传给我的是5
>>>我没有参数的
>>>ending......
View Code

        运行的时候几乎同时打印了三条结果,然后等待约5s再停止,如果是单线程的话就要8s的时间才能运行完,这样做提升了效率。其次这个程序一共有多少线程呢?答案是3个,除了t1和t2两个实例化得到的子线程,还有程序本身的主线程,还记得那句话吗?一个程序至少有一个进程,一个进程至少有一个线程。

        继承方式调用线程: 

import time
import threading

class Mythread(threading.Thread):
    def __init__(self,n):
        threading.Thread.__init__(self)
        self.n = n

    def func1(self):
        print('传给我的是%s'%self.n)
        time.sleep(self.n)

    def run(self):   ##定义每个线程要运行的函数
        self.func1()

if __name__ == '__main__':
    t1 = Mythread(2)
    t2 = Mythread(5)

    t1.start()
    t2.start()

>>>传给我的是2
>>>传给我的是5
View Code

 

       也是直接打印两条结果,然后等待5s程序结束。一般这种方式很少用到,都是直接调用比较快。如果不穿参数的话,__init__方法可以不用写,但是run函数一定要写的,至于为什么,你可以通过找继承的父类去推,这里就不啰嗦了。

       线程的join方法

     join是线程实例化后也具有的方法,像上边的start一样,那么join有什么作用呢?

 

import threading
from time import ctime,sleep

def music(name):

        print ("Begin listening to %s. %s" %(name,ctime()))
        sleep(2)
        print("end listening %s"%ctime())


def moive(title):

        print ("Begin recording the %s! %s" %(title,ctime()))
        sleep(5)
        print('end recording %s'%ctime())

threads = []

t1 = threading.Thread(target=ListenMusic,args=('毛不易的歌',))
t2 = threading.Thread(target=RecordBlog,args=('白蛇',))

threads.append(t1)
threads.append(t2)

if __name__ == '__main__':

    for t in threads:
        t.start()
        #t.join()#串行
    # t.join()

    # t1.join()

    # t2.join()########分别试一下这几种join位置下的结果
    print('endtime is %s'%ctime())
View Code

 

      我们逐个分析,不加join时:

Begin listening to 毛不易的歌. Thu Apr 11 17:47:01 2019
Begin recording the 白蛇! Thu Apr 11 17:47:01 2019
endtime is Thu Apr 11 17:47:01 2019
end listening Thu Apr 11 17:47:04 2019
end recording Thu Apr 11 17:47:06 2019

       加上一个t1.join时:

Begin listening to 毛不易的歌. Thu Apr 11 17:49:42 2019
Begin recording the 白蛇! Thu Apr 11 17:49:42 2019
end listening Thu Apr 11 17:49:45 2019
endtime is Thu Apr 11 17:49:45 2019
end recording Thu Apr 11 17:49:47 2019

       注意endtime的变化,然后去试试其他情况就可以知道(注意for循环里面和外面的t的区别),join作用就是在子线程完成运行之前,这个子线程的父线程将一直被阻塞。

       线程的setDaemon方法

      通过join我们可以阻塞线程,那么setDaemon就刚好相反,就是将线程声明为守护线程,但是必须在start() 方法调用之前设置, 如果不设置为守护线程程序会被无限挂起。当我们 在程序运行中,执行一个主线程,如果主线程又创建一个子线程,主线程和子线程 就分兵两路,分别运行,那么当主线程完成想退出时,会检验子线程是否完成。如 果子线程未完成,则主线程会等待子线程完成后再退出。但是有时候我们需要的是 只要主线程完成了,不管子线程是否完成,都要和主线程一起退出,这时就可以 用setDaemon方法啦

import threading
from time import ctime,sleep

def music(name):

        print ("Begin listening to %s. %s" %(name,ctime()))
        sleep(2)
        print("end listening %s"%ctime())


def moive(title):

        print ("Begin recording the %s! %s" %(title,ctime()))
        sleep(5)
        print('end recording %s'%ctime())

threads = []

t1 = threading.Thread(target=ListenMusic,args=('毛不易的歌',))
t2 = threading.Thread(target=RecordBlog,args=('白蛇',))

threads.append(t1)
threads.append(t2)

if __name__ == '__main__':
    #t1.setDaemon(True)
    t2.setDaemon(True)
    for t in threads:
        #t.setDaemon(True)   #一定要在start前
        t.start()
        #t.join()#串行
    # t.join()

    t1.join()

    # t2.join()########分别试一下这几种join位置下的结果
    print('endtime is %s'%ctime())
View Code

        看一下结果,白蛇没看完就停了

Begin listening to 毛不易的歌. Thu Apr 11 18:03:42 2019
Begin recording the 白蛇! Thu Apr 11 18:03:42 2019
end listening Thu Apr 11 18:03:45 2019
endtime is Thu Apr 11 18:03:45 2019

       其他方法

# run():  线程被cpu调度后自动执行线程对象的run方法
# start():启动线程活动。
# isAlive(): 返回线程是否活动的。
# getName(): 返回线程名。
# setName(): 设置线程名。

threading模块提供的一些方法:
# threading.currentThread(): 返回当前的线程变量。
# threading.enumerate(): 返回一个包含正在运行的线程的list。正在运行指线程启动后、结束前,不包括启动前和终止后的线程。
# threading.activeCount(): 返回正在运行的线程数量,与len(threading.enumerate())有相同的结果。

       基本的线程就是这样使用的,后面讲的就是同步和异步的概念,还有各种锁,慢慢来。

 

 

 

 

 

 

  

posted @ 2019-04-11 18:11 彭方炎QAQ 阅读(...) 评论(...) 编辑 收藏