开发者社区> 全能杰哥> 正文
阿里云
为了无法计算的价值
打开APP
阿里云APP内打开

猪行天下之Python基础——9.2 Python多线程与多进程(中)(一)

简介: 内容简述: 1、threading模块详解 2、queue模块详解
+关注继续查看

1、threading模块详解


Python提供的与线程操作相关的模块,网上有很多资料还是用的thread模块,在3.x版本中已经使用threading来替代thread,如果你在python 2.x版本想使用threading的话,可以使用dummy_threading模块


① threading模块提供的可直接调用的函数


  • active_count():获取当前活跃(alive)线程的个数。


  • current_thread():获取当前的线程对象。


  • get_ident():返回当前线程的索引,一个非零的整数(3.3新增)。


  • enumerate():获取当前所有活跃线程的列表。


  • main_thread():返回主线程对象(3.4新增)。


  • settrace(func):设置一个回调函数,在run()执行之前被调用。


  • setprofile(func):设置一个回调函数,在run()执行完毕之后调用。


  • stack_size():返回创建新线程时使用的线程堆栈大小。


  • threading.TIMEOUT_MAX:堵塞线程时间最大值,超过这个值会栈溢出。


② 线程局部变量(Thread-Local Data)


问题引入:


在一个进程内所有的线程共享进程的全局变量,线程间共享数据很方便但是每个线程都可以随意修改全局变量,可能会引起线程安全问题。


解决方法:


对于这种线程私有数据,最简单的方法就是对变量加锁或使用局部变量,只有线程自身可以访问,其他线程无法访问。除此之外还可以使用threading模块为我们提供的ThreadLocal变量,它本身是一个全局变量,但是线程们却可以使用它来保存私有数据。


用法简介:


定义一个全局变量:data = thread.local(),然后就可以往里面存数据啦,比如data.num = xxx,但是有一点要注意:如果data里没有设置对应的属性,直接取会报AttributeError异常,使用时可以捕获这个异常或先调用hasattr(对象,属性)判断对象中是否有该属性!使用代码示例如下:


import threading
import random
data = threading.local()
def show(d):
    try:
        num = d.num
    except AttributeError:
        print("线程 %s 还未设置该属性!" % threading.current_thread().getName())
    else:
        print("线程 %s 中该属性的值为 = %s" % (threading.current_thread().getName(), num))
def thread_call(d):
    show(d)
    d.num = random.randint(1, 100)
    show(d)
if __name__ == '__main__':
    show(data)
    data.num = 666
    show(data)
    for i in range(2):
        t = threading.Thread(target=thread_call, args=(data,), name="Thread " + str(i))
        t.start()


运行结果如下


线程 MainThread 还未设置该属性!
线程 MainThread 中该属性的值为 = 666
线程 Thread 0 还未设置该属性!
线程 Thread 0 中该属性的值为 = 80
线程 Thread 1 还未设置该属性!
线程 Thread 1 中该属性的值为 = 17


不同线程访问这个ThreadLocal变量,返回的都是不一样的值,原理:


threading.local()实例化一个全局对象,这个全局对象里有一个大字典,键值为两个弱引用对象{线程对象,字典对象},然后可以通过current_thread()获得当前的线程对象,然后根据这个对象可以拿到对应的字典对象,然后进行参数的读或者写。


③ 线程对象(threading.Thread)


创建新线程的两种方式:


  • 1.直接创建threading.Thread对象并把调用对象作为参数传入


  • 2.继承threading.Thread类重写run()方法


使用代码示例(验证单线程快还是多线程快):


import threading
import time
def catch_fish():
    pass
def one_thread():
    start_time = time.time()
    for i in range(1, 1001):
        catch_fish()
    end_time = time.time()
    print("单线程测试 耗时 === %s" % str(end_time - start_time))
def muti_thread():
    start_time = time.time()
    for i in range(1, 1001):
        threading.Thread(target=catch_fish()).start()
    end_time = time.time()
    print("多线程测试 耗时 === %s" % str(end_time - start_time))
if __name__ == '__main__':
    # 单线程
    threading.Thread(one_thread()).start()
    # 多线程
    muti_thread()


运行结果如下:


单线程测试 耗时 === 0.00011301040649414062
多线程测试 耗时 === 0.07665514945983887


从输出结果可以看到,多线程反而比单线程要慢,原因是前面介绍过的Python中的全局解释器锁(GIL), 使得任何时候仅有一个线程在执行。


Thread类构造函数


def __init__(self, group=None, target=None, name=None,
                 args=(), kwargs=None, *, daemon=None):


构造函数参数依次是:


  • group:线程组


  • target:要执行的函数


  • name:线程名字


  • args/kwargs:要传入的函数的参数


  • daemon:是否为守护线程


相关属性与函数:


  • start():启动线程,只能调用一次


  • run():线程执行的操作,可继承Thread重写,参数可从args和kwargs获取;


  • join([timeout]):堵塞调用线程,直到被调用线程运行结束或超时;如果
    没设置超时时间会一直堵塞到被调用线程结束。


  • name/getName():获得线程名;


  • setName():设置线程名;


  • ident:线程是已经启动,未启动会返回一个非零整数;


  • is_alive():判断是否在运行,启动后,终止前;


  • daemon/isDaemon():线程是否为守护线程;


  • setDaemon():设置线程为守护线程;


④ Lock(指令锁)与RLock(可重入锁)


在概念那里就讲了,多个进程并发的访问临界资源可能会引起线程同步安全问题,写个简单的例子,然后再引入同步锁。代码示例如下:


import threading
file_name = "test.txt"
# 定义一个写入文件的方法
def write_to_file(msg):
    try:
        with open(file_name, "a+", encoding="utf-8") as f:
            f.write(msg + "\n")
    except OSError as reason:
        print(str(reason))
class MyThread(threading.Thread):
    def __init__(self, msg):
        super().__init__()
        self.msg = msg
    def run(self):
        write_to_file(self.name + "~" + self.msg)
if __name__ == '__main__':
    for i in range(1, 21):
        t = MyThread(str(i)).start()


运行结果如下


# test.txt文件内容
Thread-1~1
Thread-5~5
Thread-3~3
Thread-2~2
Thread-4~4
Thread-6~6
Thread-7~7
Thread-8~8
Thread-10~10
Thread-9~9
Thread-11~11
Thread-13~13
Thread-12~12
Thread-14~14
Thread-15~15
Thread-16~16
Thread-17~17
Thread-19~19
Thread-20~20
Thread-18~18

发现结果并没有按照我们预想的1-20那样顺序打印,而是乱的,threading模块中提供了两个类来确保多线程共享资源的访问:「Lock」 和 「RLock」。


版权声明:本文内容由阿里云实名注册用户自发贡献,版权归原作者所有,阿里云开发者社区不拥有其著作权,亦不承担相应法律责任。具体规则请查看《阿里云开发者社区用户服务协议》和《阿里云开发者社区知识产权保护指引》。如果您发现本社区中有涉嫌抄袭的内容,填写侵权投诉表单进行举报,一经查实,本社区将立刻删除涉嫌侵权内容。

相关文章
Python多进程multiprocessing使用示例
来源:http://outofmemory.cn/code-snippet/2267/Python-duojincheng-multiprocessing-usage-example 来源:http://www.jb51.net/article/67116.htm 来源:http://blog.csdn.net/qdx411324962/article/details/468104
3663 0
练习--python中的Queue与多进程(multiprocessing)
按官方说法: This module is OBSOLETE and is only provided on PyPI to support old projects that still use it.
1042 0
Python标准库11 多进程探索 (multiprocessing包)
原文:Python标准库11 多进程探索 (multiprocessing包) 作者:Vamei 出处:http://www.cnblogs.com/vamei 欢迎转载,也请保留这段声明。谢谢!   在初步了解Python多进程之后,我们可以继续探索multiprocessing包中更加高级的工具。
1190 0
Python标准库10 多进程初步 (multiprocessing包)
作者:Vamei 出处:http://www.cnblogs.com/vamei 欢迎转载,也请保留这段声明。谢谢!   我们已经见过了使用subprocess包来创建子进程,但这个包有两个很大的局限性:1) 我们总是让subprocess运行外部的程序,而不是运行一个Python脚本内部编写的函数。
862 0
+关注
233
文章
0
问答
文章排行榜
最热
最新
相关电子书
更多
低代码开发师(初级)实战教程
立即下载
阿里巴巴DevOps 最佳实践手册
立即下载
冬季实战营第三期:MySQL数据库进阶实战
立即下载