博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
【Python爬虫学习笔记9】threading多线程
阅读量:7008 次
发布时间:2019-06-28

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

多线程简介

多线程,即允许程序多个线程并发地执行。多线程是为了同步完成多项任务,借助提高资源使用效率来提高系统的效率。最简单的比喻多线程就像火车的每一节车厢,而进程则是火车。车厢离开火车是无法跑动的,同理火车也不可能只有一节车厢。多线程的出现就是为了提高效率。

threading 模块

在python中有专门用于提供多线程编程的模块——threading,其中最常用的类就是Thread类,通常初始化时只需要传入用于线程执行的目标函数名(要注意是函数名,而不是函数,否则就会传入原函数的返回值)。示例如下:

## 简单多线程使用示例import threadingimport time# 定义待执行的多线程函数def eating():    for num in range(3):        print('%s is eating...%s',(threading.current_thread(),num))        time.sleep(1)def drinking():    for num in range(3):        print('%s is drinking...%s',(threading.current_thread(),num))        time.sleep(1)# 多线程主函数——实例化两个线程并调用start()方法运行def thread_main():    t1 = threading.Thread(target=eating)    t2 = threading.Thread(target=drinking)    t1.start()    t2.start()if __name__ == '__main__':    thread_main()"""Output:%s is eating...%s (
, 0)%s is drinking...%s (
, 0)%s is eating...%s (
, 1)%s is drinking...%s (
, 1)%s is eating...%s (
, 2)%s is drinking...%s (
, 2)"""

从运行结果中可以看到这两个线程交替地执行,实际上这是一个并发(速度由计算机而定)的过程,这相比于顺序执行的速度会快很多。这里还要说明的地方是,我们可以使用threading.current_thread()方法来获取当前执行的线程名,也还可以用threading.enumerate()来获取当前程序存在的线程数。

另外我们也可以基于类的编程把其封装成一个个的线程类,继承threading.Thread类并重写其中的run()方法。比如上例的类改写如下:

## 基于类的多线程编程示例import threadingimport time# 定义线程类继承Thread类并重写其中的run()方法class EatingThread(threading.Thread):    def run(self):        for num in range(3):            print('%s is eating...%s',(threading.current_thread(),num))            time.sleep(1)class DrinkingThread(threading.Thread):    def run(self):        for num in range(3):            print('%s is drinking...%s',(threading.current_thread(),num))            time.sleep(1)def thread_main():    t1 = EatingThread()    t2 = DrinkingThread()    t1.start()    t2.start()if __name__ == '__main__':    thread_main()

锁机制

在了解锁机制前,我们先来看一下下面这个例子:

## 使用多线程进行加法运算import threading# 定义全局变量VALUEVALUE = 0# 定义加法线程函数def add_value():    global VALUE    for x in range(1000000):        VALUE += 1    print('value = ', VALUE)# 定义两个线程并发执行加法操作def add_thread_main():    for x in range(2):        t = threading.Thread(target=add_value)        t.start()if __name__ == '__main__':    add_thread_main()"""Output:value =  1147074value =  1211397"""

上面的示例按照我们的逻辑看来应该是依次输出1000000和2000000,但结果并不是这样的,这就是常说的多线程共享全局变量问题。其实在我们执行线程时,执行的顺序是不一定的,也就是说有时候可能重合在一起执行,因而导致有时虽二者都对共享变量进行了一次加法(即本应加两次)而实际上只真正加了一次。

而为了解决这样的问题,threading模块提供了一个Lock类,这个类可以在某个线程访问某个变量的时候加锁,其他线程此时不能访问该变量,直到加锁线程处理完控制变量并把锁释放了,其他线程才能进行访问处理。锁机制使用起来也很简单,由于是多个线程访问共享变量,因而需设置一个全局的Lock类对象,然后在访问前后分别使用Lock类的acquire()方法加锁和release()方法释放锁。

上述例子使用锁机制仅需做以下几处的修改:

1.定义全局变量:

gLock = threading.Lock() 

2.在修改共享变量前后加锁和释放锁

gLock.acquire()

for x in range(1000000):
      VALUE += 1
gLock.release()

 

如此一来,输出结果便是:

value =  1000000

value =  2000000

在锁机制这里还要提醒的是,加锁和释放锁都是需要消耗内存空间的,因此不要频繁使用锁,仅在涉及修改和写操作的时候用,而访问读取等操作则不必要使用。

 


转载于:https://www.cnblogs.com/Unikfox/p/9703885.html

你可能感兴趣的文章
尽量避免把弹窗加在window上,可以考虑把弹窗封装到控制器里面
查看>>
opencv中的基本拼接
查看>>
Unity AssetBundle爬坑手记
查看>>
Unity 单例写法
查看>>
JQuery与JS对象相互转换
查看>>
MySQL技巧(二)——无限级分类表设计
查看>>
android 学习笔记(四) 4.1 java编程基础
查看>>
C#时间操作总结
查看>>
Java 之异常
查看>>
XStream
查看>>
java中的匿名内部类总结
查看>>
使用STL处理分支限界法处理最优装载问题
查看>>
update、commit、trancate,delete
查看>>
击败二分查找法——快速检索和插值检索
查看>>
用户获取mac地址的方法
查看>>
js精确计算(js浮点数精度问题)
查看>>
MIME Type和Content-Type
查看>>
springboot filter and interceptor
查看>>
day06 多表查询
查看>>
shell中$0,$?,$!等的特殊用法
查看>>