多线程的建立与使用


目录

  1. 生成线程的三种方法
  2. 单线程与多线程对比
  3. 守护线程的设置

1 生成线程的三种方法

三种方式分别为:

  1. 创建一个Thread实例,传给它一个函数
  2. 创建一个Thread实例,传给它一个可调用的类实例
  3. 派生Thread的子类,并创建子类的实例
# There are three ways to create a thread
# The first is create a thread instance, and pass a function
# The second one is create a thread instance, and pass the callable instance(obj)
# The third one is create a subclass of Thread, then generate an instance

1.1 创建Thread实例并传递一个函数

在Thread类实例化时,将函数(及其参数)传入,下面的例子中,在实例化Thread类的时候传入了一个函数loop及其参数,生成了一个线程的实例,再启动线程。

 # Method one: pass function
from threading import Thread
from time import sleep, ctime loops = [4, 2] def loop(nloop, nsec):
print('start loop', nloop, 'at', ctime())
sleep(nsec)
print('loop', nloop, 'done at:', ctime()) def main():
print('starting at:', ctime())
threads = []
nloops = range(len(loops)) # Create all threads
for i in nloops:
t = Thread(target=loop, args=(i, loops[i]))
threads.append(t)
for i in threads:
i.start()
for i in threads:
i.join()
print('All DONE at:', ctime()) if __name__ == '__main__':
main()

1.2 创建Thread实例并传递一个可调用的类实例

同样,在Thread类实例化时,类(及其参数)传入,下面的例子中,在实例化Thread类的时候传入了一个可调用(__call__函数使类变为可调用)的类实例ThreadFunc,且完成初始化传给target=,此时生成了一个线程的实例,随后启动线程。

 # Method two: pass object

 from threading import Thread
from time import sleep, ctime loops = [4, 2] class ThreadFunc(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('loop', nloop, 'done at:', ctime()) def main():
print('starting at:', ctime())
threads = []
nloops = range(len(loops)) # Create all threads
for i in nloops:
t = Thread(target=ThreadFunc(loop, (i, loops[i]), loop.__name__))
threads.append(t)
for i in threads:
i.start()
for i in threads:
i.join()
print('All DONE at:', ctime()) if __name__ == '__main__':
main()

1.3 派生Thread子类并生成子类实例

以Thread为基类,派生出一个子类,在子类中重定义run方法,最终生成一个线程实例进行调用。下面的例子中,生成了一个子类MyThread,同时重定义run函数,在初始化时接收一个func参数作为调用函数。

 # Method three: by subclass

 from threading import Thread
from time import sleep, ctime loops = [4, 2] class MyThread(Thread):
def __init__(self, func, args, name=''):
Thread.__init__(self)
self.name = name
self.func = func
self.args = args def run(self):
self.func(*self.args) def loop(nloop, nsec):
print('start loop', nloop, 'at', ctime())
sleep(nsec)
print('loop', nloop, 'done at:', ctime()) def main():
print('starting at:', ctime())
threads = []
nloops = range(len(loops)) # Create all threads
for i in nloops:
t = MyThread(loop, (i, loops[i]), loop.__name__)
threads.append(t)
for i in threads:
i.start()
for i in threads:
i.join()
print('All DONE at:', ctime()) if __name__ == '__main__':
main()

2 单线程与多线程对比

利用单线程与多线程分别进行斐波那契,阶乘与累加操作,此处加入了sleep进行计算延迟,这是由于Python解释器的GIL特性使得Python对于计算密集型的函数并没有优势,而对于I/O密集型的函数则优化性能较好。

 from threading import Thread
import time
from time import ctime class MyThread(Thread):
"""
Bulid up a Module to make this subclass more general
And get return value by add a function named 'getResult()'
"""
def __init__(self, func, args, name=''):
Thread.__init__(self)
self.name = name
self.func = func
self.args = args def getResult(self):
return self.res def run(self):
print('Starting', self.name, 'at:', ctime())
# Call function here and calculate the running time
self.res = self.func(*self.args)
print(self.name, 'finished at:', ctime()) def fib(x):
time.sleep(0.005)
if x < 2:
return 1
return (fib(x-2) + fib(x-1)) def fac(x):
time.sleep(0.1)
if x < 2:
return 1
return (x * fac(x-1)) def sumx(x):
time.sleep(0.1)
if x < 2:
return 1
return (x + sumx(x-1)) funcs = [fib, fac, sumx]
n = 12 def main():
nfuncs = range(len(funcs))
print('***SINGLE THREADS')
for i in nfuncs:
print('Starting', funcs[i].__name__, 'at:', ctime())
print(funcs[i](n))
print(funcs[i].__name__, 'finished at:', ctime()) print('\n***MULTIPLE THREADS')
threads = []
for i in nfuncs:
t = MyThread(funcs[i], (n,), funcs[i].__name__)
threads.append(t)
for i in nfuncs:
threads[i].start()
for i in nfuncs:
threads[i].join()
print(threads[i].getResult())
print('All DONE') if __name__ == '__main__':
main()

第 1-24 行,导入所需模块,并派生线程的子类,定义一个返回函数用于返回结果,

第 26-45 行,分别定义斐波那契,阶乘与累加函数,

最后在主函数中分别运行两种模式的计算,得到结果

***SINGLE THREADS
Starting fib at: Tue Aug 1 20:17:47 2017
233
fib finished at: Tue Aug 1 20:17:50 2017
Starting fac at: Tue Aug 1 20:17:50 2017
479001600
fac finished at: Tue Aug 1 20:17:51 2017
Starting sumx at: Tue Aug 1 20:17:51 2017
78
sumx finished at: Tue Aug 1 20:17:52 2017 ***MULTIPLE THREADS
Starting fib at: Tue Aug 1 20:17:52 2017
Starting fac at: Tue Aug 1 20:17:52 2017
Starting sumx at: Tue Aug 1 20:17:52 2017
sumx finished at: Tue Aug 1 20:17:53 2017
fac finished at: Tue Aug 1 20:17:53 2017
fib finished at: Tue Aug 1 20:17:54 2017
233
479001600
78
All DONE

从结果中可以看出单线程耗时5秒,而多线程耗时2秒,优化了程序的运行速度。

Note: 再次注明,Python的多线程并未对计算性能有所提升,此处是由于加入了sleep的等待,因此使得Python的GIL发挥其优势。

守护线程的设置

守护线程一般属于后台无限循环的程序,主线程会在所有非守护线程结束之后,自动关闭还在运行的守护线程,而不会等待它的无限循环完成。守护线程的设置只要将线程实例的daemon设置为True即可,默认是False。

 import threading
import time
import random class MyThread(threading.Thread):
def __init__(self, count):
threading.Thread.__init__(self)
print('%s: There are %d numbers need to be counted' % (self.name, count))
self.count = count def run(self):
name = self.name
for i in range(0, self.count):
print('%s counts %d' % (name, i))
time.sleep(1)
print('%s finished' % name) def main():
print('-------Starting-------')
count = random.randint(4, 7)
t_1 = MyThread(count*count)
t_2 = MyThread(count)
t_1.daemon = True
t_2.daemon = False
t_1.start()
t_2.start()
time.sleep(3)
print('---Main Thread End---') if __name__ == '__main__':
main()

第 5-16 行,派生一个线程子类,run函数实现每秒计数一次的功能

第 19-29 行,在主函数中,分别生成两个线程实例,其中t_1计数量较大,设为守护线程,t_2计数量较小,设为非守护线程,线程均不挂起,主线程在3秒后会结束。

运行得到结果

-------Starting-------
Thread-1: There are 36 numbers need to be counted
Thread-2: There are 6 numbers need to be counted
Thread-1 counts 0
Thread-2 counts 0
Thread-1 counts 1
Thread-2 counts 1
Thread-1 counts 2
Thread-2 counts 2
Thread-1 counts 3
---Main Thread End---
Thread-2 counts 3
Thread-1 counts 4
Thread-2 counts 4
Thread-1 counts 5
Thread-2 counts 5
Thread-1 counts 6
Thread-2 finished

运行主函数后可以看到,两个线程启动后,计数3次时,主线程已经结束,而这时由于t_2是非守护线程,因此主线程挂起等待t_2的计数结束之后,杀掉了还在运行的守护线程t_1,并且退出了主线程。

相关阅读


1. 基本概念

2. threading 模块

参考链接


《Python 核心编程 第3版》

Python的并发并行[1] -> 线程[1] -> 多线程的建立与使用的更多相关文章

  1. Python的并发并行[1] -> 线程[3] -> 多线程的同步控制

    多线程的控制方式 目录 唤醒单个线程等待 唤醒多个线程等待 条件函数等待 事件触发标志 函数延迟启动 设置线程障碍 1 唤醒单个线程等待 Condition类相当于一把高级的锁,可以进行一些复杂的线程 ...

  2. Python的并发并行[1] -> 线程[2] -> 锁与信号量

    锁与信号量 目录 添加线程锁 锁的本质 互斥锁与可重入锁 死锁的产生 锁的上下文管理 信号量与有界信号量 1 添加线程锁 由于多线程对资源的抢占顺序不同,可能会产生冲突,通过添加线程锁来对共有资源进行 ...

  3. Python的并发并行[1] -> 线程[0] -> threading 模块

    threading模块 / threading Module 1 常量 / Constants Pass 2 函数 / Function 2.1 setprofile()函数 函数调用: thread ...

  4. 并发 并行 进程 线程 协程 异步I/O python async

    一些草率不精确的观点: 并发: 一起发生,occurence: sth that happens. 并行: 同时处理. parallel lines: 平行线.thread.join()之前是啥?落霞 ...

  5. Python的并发并行[2] -> 队列[1] -> 使用队列进行任务控制

    使用队列进行任务控制 1 FIFO与LIFO队列 FIFO(First In First Out)与LIFO(Last In First Out)分别是两种队列形式,在FIFO中,满足先入先出的队列方 ...

  6. Python 之并发编程之线程下

    七.线程局部变量 多线程之间使用threading.local 对象用来存储数据,而其他线程不可见 实现多线程之间的数据隔离 本质上就是不同的线程使用这个对象时,为其创建一个只属于当前线程的字典 拿空 ...

  7. Python的并发并行[4] -> 并发[0] -> 利用线程池启动线程

    利用线程池启动线程 submit与map启动线程 利用两种方式分别启动线程,同时利用with上下文管理来对线程池进行控制 from concurrent.futures import ThreadPo ...

  8. Python 之并发编程之线程上

    一.线程概念 进程是资源分配的最小单位 线程是计算机中调度的最小单位 多线程(即多个控制线程)的概念是,在一个进程中存在多个控制线程,多个控制线程共享该进程的地址空间,相当于一个车间内有多条流水线,都 ...

  9. Python的并发并行[0] -> 基本概念

    基本概念 / Basic Concept  快速跳转 进程 / Process 线程 / Thread 协程 / Coroutine 全局解释器锁 / Global Interpreter Lock ...

随机推荐

  1. ASP.NET Core API ---状态码

    摘录自:https://www.cnblogs.com/cgzl/p/9047626.html 状态码是非常重要的,因为只有状态码会告诉API的消费者: 请求是否如预期的成功,或者失败 如果出现了错误 ...

  2. hp raid配置

    http://www.cnblogs.com/zhangxinglong/p/5585139.html [root@192e168e100e27 ~]# fdisk -l Disk /dev/nvme ...

  3. leetcode 179. 最大数 解题报告

    给定一组非负整数,重新排列它们的顺序使之组成一个最大的整数. 示例 1: 输入: [10,2] 输出: 210 示例 2: 输入: [3,30,34,5,9] 输出: 9534330 说明: 输出结果 ...

  4. Day2 Activity生命周期/启动模式/最佳实践

    Android是使用任务(Task)来管理活动的,这个栈被称作返回栈(Back Stack). Activity类中定义了7个回调方法: onCreate().在活动第一次被创建时调用,应该在这个方法 ...

  5. 搭建 MongoDB分片(sharding) / 分区 / 集群环境

    1. 安装 MongoDB 三台机器 关闭防火墙 systemctl stop firewalld.service 192.168.252.121 192.168.252.122 192.168.25 ...

  6. mysql安装目录、配置文件存放位置

    linux系统下,如何知道mysql使用的配置文件到底是哪个呢?linux自带的mysql的安装目录又是什么呢?数据存放在什么目录下? 1.linux系统自带的mysql,其安装目录及数据目录查看方法 ...

  7. hadoop-搭建(转)--亲测好用 (一)

    1)JDK软件 下载地址:http://www.oracle.com/technetwork/java/javase/index.html 2)Hadoop软件 下载地址:http://hadoop. ...

  8. 【bzoj2079】[Poi2010]Guilds 构造结论题

    题目描述 Zy皇帝面临一个严峻的问题,两个互相抵触的贸易团体,YYD工会和FSR工会,他们在同一时间请求在王国各个城市开办自己的办事处.这里有n个城市,其中有一些以双向马路相连,这两个工会要求每个城市 ...

  9. AtCoder keyence2019 E Connecting Cities

    keyence2019_e $N$ 个节点的无向图 $G$,节点 $i,j$ 之间的边权值为 $|i - j| \times D + A_i + A_j$ . 求最小生成树(Minimum Spann ...

  10. [codeforces] 25E Test || hash

    原题 给你三个字符串,找一个字符串(它的子串含有以上三个字符串),输出此字符串的长度. 先暴力判断是否有包含,消减需要匹配的串的数量.因为只有三个字符串,所以暴力枚举三个串的位置关系,对三个串跑好哈希 ...