博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
python多线程
阅读量:5815 次
发布时间:2019-06-18

本文共 4293 字,大约阅读时间需要 14 分钟。

hot3.png

多线程

任务可以由多进程完成,也可以由一个进程内的多线程完成。

我们前面提到了进程是由若干线程组成的,一个进程至少有一个线程。
由于线程是操作系统直接支持的执行单元,因此,高级语言通常都内置多线程的支持,Python也不例外,并且,Python的线程是真正的Posix Thread,而不是模拟出来的线程。Python的标准库提供了threading模块。

创建

在python中多线程的实现方法有2种。

  1. 将要执行的方法作为参数传递给Thread的构造方法。
    1234567891011121314
    import threadingdef worker(args):    print("开始子进程 {0}".format(args))    print("结束子进程 {0}".format(args))if __name__ == '__main__':    print("start main")    t1 = threading.Thread(target=worker, args=(1,))    t2 = threading.Thread(target=worker, args=(2,))    t1.start()    t2.start()    print("end main")

输出结果

123456
start main开始子进程 1结束子进程 1开始子进程 2结束子进程 2end main

 

  1. 从Thread继承,并重写run()方法。
    123456789101112131415161718192021
    import threadingimport timeclass Hello(threading.Thread):    def __init__(self, args):        super(Hello, self).__init__()    #注意:要显式的调用父类的初始化函数        self.args = args    def run(self):        print("开始子进程 {0}".format(self.args))        time.sleep(1)        print("结束子进程 {0}".format(self.args))if __name__ == '__main__':    a = 1    print("start main")    t1 = Hello(1)    t2 = Hello(2)    t1.start()    t2.start()    print("end main")

输出结果

123456
start main开始子进程 1开始子进程 2end main结束子进程 1结束子进程 2

 

模块函数

  • threading.active_count()
    返回当前存活的thread对象的数量。返回的数量和enumerate()列表长度相等。
  • threading.current_thread()
    返回当前线程对象。
  • threading.get_ident()
    返回当前线程的thread identifier。这是个非零的整数。该值无特殊含义。
  • threading.enumerate()
    以列表形式返回当前存活的所有线程对象。列表中包括守护线程和由current_thread()创建的虚拟线程对象,不包括已结束和还没开始的线程。
  • threading.main_thread()
    返回主线程对象。正常情况下,主线程由python解释器启动。

    对象方法

  • start()
    启动线程,调用run方法。同一线程对象多次调用将会出现RuntimeError错误。
  • run()
    启动线程
  • join(timeout=None)
    等待线程对象结束。
  • is_alive()
    线程是否活着

    对象属性

  • name
    线程的名称,无特殊意义。
  • daemon
    布尔值,表示该线程是否为守护线程。设置该值必须在调用run()方法前,否则会报RuntimeError错误。假如主线程不是守护线程,该主线程创建的所有子线程该值为False。

    线程锁

    多线程和多进程最大的不同在于,多进程中,同一个变量,各自有一份拷贝存在于每个进程中,互不影响,而多线程中,所有变量都由所有线程共享,所以,任何一个变量都可以被任何一个线程修改,因此,线程之间共享数据最大的危险在于多个线程同时改一个变量,把内容给改乱了。
    这种情况下通过线程锁保证修改不会发生冲突。
    锁的创建通过threading.Lock()进行,操作类似于多进程。
    123456789101112131415
    import threadingimport timedef worker(name, lock):    with lock:        print("start {0}".format(name))        time.sleep(5)        print("end {0}".format(name))if __name__ == "__main__":    lock = threading.Lock()    t1 = threading.Thread(target=worker, args=("worker1", lock))    t2 = threading.Thread(target=worker, args=("worker2", lock))    t1.start()    t2.start()

输出结果

1234
start worker1end worker1start worker2end worker2

 

当然,获取锁也可以通过lock.acquire()完成,释放锁通过lock.release()完成。

12345678
def worker(name, lock):    lock.acquire()    try:        print("start {0}".format(name))        time.sleep(5)        print("end {0}".format(name))    finally:        lock.release()

 

  • 锁的好处
    确保了某段关键代码只能由一个线程从头到尾完整地执行。
  • 坏处
    阻止了多线程并发执行,包含锁的某段代码实际上只能以单线程模式执行,效率就大大地下降了。
    由于可以存在多个锁,不同的线程持有不同的锁,并试图获取对方持有的锁时,可能会造成死锁,导致多个线程全部挂起,既不能执行,也无法结束,只能靠操作系统强制终止。

    线程共享内存

    多线程和多进程不同之处在于多线程本身就是可以和父进程共享内存的,这也是为什么其中一个线程挂掉以后,为什么其他线程也会死掉的道理。
    1234567891011121314
    import threadingl = list()l += range(1, 4)def worker():    l.append("GOD")if __name__ == "__main__":    t1 = threading.Thread(target=worker)    t2 = threading.Thread(target=worker)    t1.start()    t2.start()    print(l)

输出结果

1
[1, 2, 3, 'GOD', 'GOD']

 

线程池

在使用多线程处理任务时也不是线程越多越好,由于在切换线程的时候,需要切换上下文环境,依然会造成cpu的大量开销。为解决这个问题,线程池的概念被提出来了。预先创建好一个较为优化的数量的线程,让过来的任务立刻能够使用,就形成了线程池。

此处介绍threapool是一个第三方模块,需要使用pip install threadpool安装。

1234567891011121314
import threadpooldef hello(m, n, o):    print("m = {0}  n={1}  o={2}".format(m, n, o))if __name__ == '__main__':    lst_vars_1 = ['1', '2', '3']    lst_vars_2 = ['4', '5', '6']    func_var = [(lst_vars_1, None), (lst_vars_2, None)]    pool = threadpool.ThreadPool(2)    requests = threadpool.makeRequests(hello, func_var)    [pool.putRequest(req) for req in requests]    pool.wait()

 

输出结果

12
m = 1  n=2  o=3m = 4  n=5  o=6

 

  • Executor
    在python3.3之后,并行任务可以使用Executor Objects,更多内容点击查看官方介绍。
  • 关于python中多核CPU的说明
    Python的线程虽然是真正的线程,但解释器执行代码时,有一个GIL锁:Global Interpreter Lock,任何Python线程执行前,必须先获得GIL锁,然后,每执行100条字节码,解释器就自动释放GIL锁,让别的线程有机会执行。这个GIL全局锁实际上把所有线程的执行代码都给上了锁,所以,多线程在Python中只能交替执行,即使100个线程跑在100核CPU上,也只能用到1个核。
    GIL是Python解释器设计的历史遗留问题,通常我们用的解释器是官方实现的CPython,要真正利用多核,除非重写一个不带GIL的解释器。
    所以,在Python中,可以使用多线程,但不要指望能有效利用多核。如果一定要通过多线程利用多核,那只能通过C扩展来实现,不过这样就失去了Python简单易用的特点。
    Python虽然不能利用多线程实现多核任务,但可以通过多进程实现多核任务。多个Python进程有各自独立的GIL锁,互不影响。
    更多关于threading的内容可以点击查看官方介绍。

转载于:https://my.oschina.net/u/3803404/blog/1817795

你可能感兴趣的文章
d3 v4实现饼状图,折线标注
查看>>
微软的云策略
查看>>
Valid Parentheses
查看>>
性能测试之稳定性测试
查看>>
ES6的 Iterator 遍历器
查看>>
2019届高二(下)半期考试题(文科)
查看>>
nginx 301跳转到带www域名方法rewrite(转)
查看>>
AIX 配置vncserver
查看>>
windows下Python 3.x图形图像处理库PIL的安装
查看>>
【IL】IL生成exe的方法
查看>>
network
查看>>
SettingsNotePad++
查看>>
centos7安装cacti-1.0
查看>>
3个概念,入门 Vue 组件开发
查看>>
没有JS的前端:体积更小、速度更快!
查看>>
数据指标/表现度量系统(Performance Measurement System)综述
查看>>
GitHub宣布推出Electron 1.0和Devtron,并将提供无限制的私有代码库
查看>>
Angular2, NativeScript 和 React Native比较[翻译]
查看>>
论模式在领域驱动设计中的重要性
查看>>
国内首例:飞步无人卡车携手中国邮政、德邦投入日常运营
查看>>