python3网络编程之多线程

多线程类似于同时执行多个不同程序,多线程运行有如下优点:

  • 使用线程可以把占据长时间的程序中的任务放到后台去处理。
  • 用户界面可以更加吸引人,这样比如用户点击了一个按钮去触发某些事件的处理,可以弹出一个进度条来显示处理的进度
  • 程序的运行速度可能加快
  • 在一些等待的任务实现上如用户输入、文件读写和网络收发数据等,线程就比较有用了。在这种情况下我们可以释放一些珍贵的资源如内存占用等等。

线程在执行过程中与进程还是有区别的。每个独立的线程有一个程序运行的入口、顺序执行序列和程序的出口。但是线程不能够独立执行,必须依存在应用程序中,由应用程序提供多个线程执行控制。

每个线程都有他自己的一组CPU寄存器,称为线程的上下文,该上下文反映了线程上次运行该线程的CPU寄存器的状态。

指令指针和堆栈指针寄存器是线程上下文中两个最重要的寄存器,线程总是在进程得到上下文中运行的,这些地址都用于标志拥有线程的进程地址空间中的内存。

  • 线程可以被抢占(中断)。
  • 在其他线程正在运行时,线程可以暂时搁置(也称为睡眠) -- 这就是线程的退让。

线程可以分为:

  • 内核线程:由操作系统内核创建和撤销。
  • 用户线程:不需要内核支持而在用户程序中实现的线程。

Python3 线程中常用的两个模块为:

  • _thread
  • threading(推荐使用)

thread 模块已被废弃。用户可以使用 threading 模块代替。所以,在 Python3 中不能再使用"thread" 模块。为了兼容性,Python3 将 thread 重命名为 "_thread"。 

在这里我们主要讲解的是threading模块。

_thread 提供了低级别的、原始的线程以及一个简单的锁,它相比于 threading 模块的功能还是比较有限的。

threading 模块除了包含 _thread 模块中的所有方法外,还提供的其他方法:

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

除了使用方法外,线程模块同样提供了Thread类来处理线程,Thread类提供了以下方法:

  • run(): 用以表示线程活动的方法。
  • start():启动线程活动。 
  • join([time]): 等待至线程中止。这阻塞调用线程直至线程的join() 方法被调用中止-正常退出或者抛出未处理的异常-或者是可选的超时发生。
  • isAlive(): 返回线程是否活动的。
  • getName(): 返回线程名。
  • setName(): 设置线程名。

在我们理解多线程之前,我们先来看一下单线程。

参考链接:http://www.cnblogs.com/fnng/p/3670789.html

单线程:

假设在很多年前,我想听音乐和看电影一定要先排一下顺序的。

from time import ctime,sleep

def music():
    for i in range(2):
        print("I was listening to music. %s" % ctime())
        sleep(1)

def move():
    for i in range(2):
        print("I was at the movies. %s" % ctime())
        sleep(5)

if __name__ == '__main__':
    music()
    move()
    print("all over %s" % ctime())

我们先听了一首音乐,通过for循环来控制音乐的播放了两次,每一首音乐需要播放1秒钟,sleep()来控制音乐播放的时长。接着又看了一场电影,每一场电影需要5秒钟,我们通过for循环看了两遍。听完歌和看完电影以后,通过print("all over %s" % ctime())看了一下当前时间,准备结束这个娱乐活动。

运行结果:

I was listening to music. Tue Aug  1 17:08:15 2017
I was listening to music. Tue Aug  1 17:08:16 2017
I was at the movies. Tue Aug  1 17:08:17 2017
I was at the movies. Tue Aug  1 17:08:22 2017
all over Tue Aug  1 17:08:27 2017

 如果我们把music()和move()看作是音乐播放器和视频播放器的话,我们要播放什么歌曲或者电影就应该由我们自己来决定。所以,就把以上代码修改为:

from time import ctime,sleep

def music(func):
    for i in range(2):
        print("I was listening to %s. %s" % (func, ctime()))
        sleep(1)

def move(func):
    for i in range(2):
        print("I was at the %s. %s" % (func, ctime()))
        sleep(5)

if __name__ == '__main__':
    music('十年')
    move('战狼')
    print("all over %s" % ctime())

这样我们就对music()和move()进行了传参处理。

运行结果:

I was listening to 十年. Tue Aug  1 17:17:22 2017
I was listening to 十年. Tue Aug  1 17:17:23 2017
I was at the 战狼. Tue Aug  1 17:17:24 2017
I was at the 战狼. Tue Aug  1 17:17:29 2017
all over Tue Aug  1 17:17:34 2017

 

多线程:

跟着时代的发展,科技的进步,由于我们程序猿的幸苦工作,CPU也就越来越快了,我们就可以同时干多件事情,所以我们一边coding一边听歌的时代到来。

我们继续对于以上的例子进行改变,引入threadring来同时播放音乐和视频:

import threading
from time import ctime,sleep

def music(func):
    for i in range(2):
        print("I was listening to %s. %s" % (func, ctime()))
        sleep(1)

def move(func):
    for i in range(2):
        print("I was at the %s. %s" % (func, ctime()))
        sleep(5)

# 创建了threads数组 把创建好的线程t1装到threads数组中。
threads = []
# 创建线程t1,使用threading.Thread()方法,在这个方法中调用music方法target=music,args方法对music进行传参。
t1 = threading.Thread(target=music, args=('十年',))
# 把创建好的线程t1装到threads数组中
threads.append(t1)
# 接着以同样的方式创建线程t2,并把t2也装到threads数组。
t2 = threading.Thread(target=move, args=('战狼',))
threads.append(t2)

if __name__ == '__main__':
    for t in threads:
        t.setDaemon(True)
        t.start()
    print("all over %s" % ctime())

setDaemon()

setDaemon(True)将线程声明为守护线程,必须在start() 方法调用之前设置,如果不设置为守护线程程序会被无限挂起。子线程启动后,父线程也继续执行下去,当父线程执行完最后一条语句print("all over %s" % ctime())后,没有等待子线程,直接就退出了,同时子线程也一同结束。

 start()

开始线程活动。

运行结果:

I was listening to 十年. Tue Aug  1 17:31:29 2017
I was at the 战狼. Tue Aug  1 17:31:29 2017
all over Tue Aug  1 17:31:29 2017

从执行结果来看,子线程(muisc 、move )和主线程(print("all over %s" % ctime()))都是同一时间启动,但由于主线程执行完结束,所以导致子线程也终止。 

继续调整程序:

...
if __name__ == '__main__':
    for t in threads:
        t.setDaemon(True)
        t.start()

    t.join()

    print("all over %s" % ctime())

我们只对上面的程序加了个join()方法,用于等待线程终止。join()的作用是,在子线程完成运行之前,这个子线程的父线程将一直被阻塞。

PS: join()方法的位置是在for循环外的,也就是说必须等待for循环里的两个进程都结束后,才去执行主进程。

运行结果:

I was listening to 十年. Tue Aug  1 20:24:03 2017
I was at the 战狼. Tue Aug  1 20:24:03 2017
I was listening to 十年. Tue Aug  1 20:24:04 2017
I was at the 战狼. Tue Aug  1 20:24:08 2017
all over Tue Aug  1 20:24:13 2017

从执行结果可看到,music 和move 是同时启动的。

开始时间24分03秒,直到调用主进程为24分13秒,总耗时为10秒。从单线程时减少了2秒,我们可以把music的sleep()的时间调整为4秒。

...
def music(func):
for i in range(2):
print("I was listening to %s. %s" % (func, ctime()))
sleep(4)

...

运行结果:

I was listening to 十年. Tue Aug  1 20:34:59 2017
I was at the 战狼. Tue Aug  1 20:34:59 2017
I was listening to 十年. Tue Aug  1 20:35:03 2017
I was at the 战狼. Tue Aug  1 20:35:04 2017
all over Tue Aug  1 20:35:09 2017

子线程启动34分59秒,主线程运行35分09秒。

虽然music每首歌曲从1秒延长到了4秒 ,但通过多程线的方式运行脚本,总的时间没变化。

 class threading.Thread()说明:

 class threading.Thread(group=None, target=None, name=None, args=(), kwargs={}, *, daemon=None)

 这个构造函数通常会用一些关键字参数,下面我们了解下这些关键字:
        group :这个变量作为保留变量,是为了以后扩展用的,暂时可以不用考虑。
        target: 是通过run()方法调用的可调用对象。默认为无,这意味着什么都不做。
        name:线程的名字。默认情况下,一个唯一的名称是”thread-n”,的形式,其中n是一个小的十进制数。
        args:元组参数,为target所调用的。
        kwargs:关键字参数的字典,为target所调用的。
        daemon: 设置daemon是否daemon 如果没有显示设置,daemon的属性时从当前线程继承。
        如果子类重写此构造函数,它必须确保在做别的事情之前调用基类的构造函数(thread.__init__()。

线程有两种调用方式:

1.直接调用

import threading
import time

def sayhi(num): #定义每个线程要运行的函数

    print("running on number:%s" %num)

    time.sleep(3)

if __name__ == '__main__':

    t1 = threading.Thread(target=sayhi,args=(1,)) #生成一个线程实例
    t2 = threading.Thread(target=sayhi,args=(2,)) #生成另一个线程实例

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

    print(t1.getName()) #获取线程名
    print(t2.getName())

2.继承式调用

import threading
import time

class MyThread(threading.Thread):
    def __init__(self,num):
        threading.Thread.__init__(self)
        self.num = num

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

        print("running on number:%s" % self.num)

        time.sleep(3)

if __name__ == '__main__':

    t1 = MyThread(1)
    t2 = MyThread(2)
    t1.start()
    t2.start()

 

转载于:https://www.cnblogs.com/Bigtre/p/7269468.html

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值