博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Python系统编程之线程
阅读量:7175 次
发布时间:2019-06-29

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

threading模块

线程简述

线程(轻量级进程)与进程类似,不过它们是在同一个进程下执行的,并共享相同的上下文。可以将它们认为是在一个主进程或"主线程"中并行运行的一些"迷你进程"。

线程包括开始、执行顺序和结束三部分。它有一个指令指针,用于记录运行的上下文。它其他线程运行时,它可以被抢占(中断)和临时挂起(睡眠/加锁)---这种做法叫做让步(yielding)。

多线程的创建

使用Thread类,可以有很多方法来创建线程,其中常用的有:

  • 创建Thread的示例,传给它一个函数;
  • 派生Thread的子类,重新run方法,并创建子类的实例。

示例1:创建Thread的实例,传给它一个函数

from threading import Threadimport timedef test():    print("---hello-world---")    time.sleep(1)for i in range(5):    #创建线程,线程执行的任务是target指定的函数,如果函数需要传入参数,则可以指定args=(),或者kwargs={}    t = Thread(target=test)    t.start()

运行结果:

---hello-world------hello-world------hello-world------hello-world------hello-world---

示例2:使用Thread子类创建线程

import threadingimport time# 自定义类继承threading类class myThread(threading.Thread):    # 重新run方法    def run(self):        for i in range(3):            time.sleep(1)            msg = "I'm " + self.name+' @ '+str(i)            print(msg)if __name__ == "__main__":    # 创建线程    t = myThread()    t.start()

运行结果:

I'm Thread-1 @ 0I'm Thread-1 @ 1I'm Thread-1 @ 2

python的threading.Thread类有一个run方法,用于定义线程的功能函数,可以在自己的线程类中覆盖该方法。而创建自己的线程实例后,通过Thread类的start方法,可以启动该线程,交给python虚拟机进行调度,当该线程获得执行的机会时,就会调用run方法执行线程。

多线程共享全局变量

在一个进程中,多个线程之间是共享全局变量的,即一个线程修改了全局变量,另外一个线程在此之后获取的这个全局变量是被修改后的。比如下面例子:

from threading import Threadimport timenum = 100def thread1():    global num    for i in range(3):        num += 1    print("I'am Thread1 ."  +  " my num is "+str(num))def thread2():    print("I'am Thread2. " +" my num is "+ str(num))t1 = Thread(target=thread1)t1.start()# 让程序睡眠1秒钟,确保线程1执行完毕。time.sleep(1)t2 = Thread(target=thread2)t2.start()

运行结果:

I'am Thread1. my num is 103I'am Thread2.  my num is 103

线程关于全局变量注意点

在一个进程内的所有线程共享全局变量,能够在不适用其他方式的前提下完成多线程之间的数据共享(这点要比多进程要好)

一个进程中的各个线程与主线程共享同一片数据空间。因此与进程相比,线程之间的信息共享和通信更加容易。在一个程序中,线程的执行是:每个线程运行一小会,然后让步给其他线程(再次排队等待更多的CPU时间)。在整个进程的执行过程中,每个线程执行它自己特定的任务,在必要时和其他线程进行通信。

当然,这种共享是有风险的。如果两个或多个线程访问同一片数据,由于数据的访问顺序不同可能导致结果不一致。这种情况叫竞态条件。辛运的是,大多数线程库都有一些同步源语,以允许线程管理器控制执行和访问。

同步和互斥锁

同步

一般在多线程代码中,总有一些函数或者代码块不希望被多个线程同时执行,如果有两个线程运行的顺序发生变化,就有可能造成代码的执行轨迹或行为不同,产生不一样的数据。这时候就需要使用同步了。

同步可以理解为协同步调,按预定的先后次序进行运行,比如你先说,我再讲。

示例1:多个线程对全局变量修改的bug

from threading import Threadimport timenum = 0    def work1():    global num    for i in range(1000000):        num += 1    print("-work1-num:%d"%num)    def work2():    global num    for i in range(1000000):        num += 1    print("-work2-num:%d"%num)t1 = Thread(target=work1)t1.start()   #time.sleep(3)t2 = Thread(target=work2)t2.start()

运行结果:

-work1-num:1105962-work2-num:1150358

这个程序是两个线程同时对全局变量num进行相加操作,但是因为多线程中线程的执行顺序是不同的,因此出现最后相加结果不是2000000的结果。

示例2:避免全局变量被修改的方法

避免上面的情况可以有很多种方法,第一种是将上面time.sleep(3)的注释去掉,就是在3秒内让线程1执行,3s内执行完毕再执行线程2对num变量进行自增。(不过这种方法跟单线程没区别,也就没有意义去创建多线程了...)

第二种就是使用轮询,代码示例如下:

from threading import Threadimport timenum = 0item = 1def work1():    global num    global item    if item == 1:        for i in range(1000000):            num += 1        item = 0    print("-work1-num:%d"%num)def work2():    global num    while True:# 轮询,一直查看条件是否满足,线程2一直在执行...        if item != 1:            for i in range(1000000):                num += 1            break    print("-work2-num:%d"%num)t1 = Thread(target=work1)t1.start()#time.sleep(3)t2 = Thread(target=work2)t2.start()

运行结果:

-work1-num:1000000-work2-num:200000

这次结果就正确的了,不过这种方法效率也不高。第三种方法就是锁了。

互斥锁

当多个线程几乎同时修改某一个共享数据的时候,需要进行同步控制

线程同步能够保证多个线程安全访问竞争资源,最简单的同步机制是引入互斥锁。

互斥锁为资源引入一个状态:锁定/非锁定

某个线程要更改共享数据时,先将其锁定,此时资源的状态为“锁定”,其他线程不能更改;直到该线程释放资源,将资源的状态变成“非锁定”,其他的线程才能再次锁定该资源。互斥锁保证了每次只有一个线程进行写入操作,从而保证了多线程情况下数据的正确性。
threading模块定义了Lock类,可以很方便地进行锁定。

#创建锁mutex = threading.Lock()#锁定mutex.acquire([blocking])#释放mutex.release()

其中,锁定方法acquire可以有一个blocking参数。

  • 如果设定blocking为True,则当前线程会堵塞,直到获取到这个锁为止(如果没有指定,那么默认为True)
  • 如果设定blocking为False,则当前线程不会堵

示例:

from threading import Thread,Lockimport timenum = 0def work1():    global num    # 上锁    mutex.acquire()       for i in range(1000000):        num += 1    # 解锁    mutex.release()    print("-work1-num:%d"%num)def work2():    global num    mutex.acquire()    for i in range(1000000):        num += 1    mutex.release()    print("-work2-num:%d"%num)# 创建一把锁,这个锁默认是没有上锁的mutex = Lock()t1 = Thread(target=work1)t1.start()#time.sleep(3)t2 = Thread(target=work2)t2.start()

运行结果:

-work1-num:1000000-work2-num:2000000

代码中定义了一把锁mutex,线程t1和线程t2都互相竞争着这把锁,谁先上锁,另一方就上不了锁而堵塞。当上锁的线程执行完毕进行解锁,堵塞的线程就争夺到上锁权而进行代码块的运行。

转载地址:http://ngbzm.baihongyu.com/

你可能感兴趣的文章
面试如何回答优化数据库
查看>>
SuperSocket与Netty之实现protobuf协议,包括服务端和客户端
查看>>
ASP.NET CORE系列【二】使用Entity Framework Core进行增删改查
查看>>
js如何返回两个数的商的整数和余数部分?
查看>>
AIDL基本使用
查看>>
MySQL中间件之ProxySQL(6):管理后端节点
查看>>
Mathematica 取整函数
查看>>
使用Java进行串口SerialPort通讯
查看>>
(转)Awsome Domain-Adaptation
查看>>
利用cwRsync客户端将Windows下文件同步到Linux
查看>>
VS2017专业版和企业版激活密钥
查看>>
ES6 对象转Map
查看>>
深入解析SQL Server高可用镜像实现原理
查看>>
Unity之MVC 模式
查看>>
解决 winform打开网页 和WebBrowser打开链接360误报拦截的问题
查看>>
Java基础加强总结(三)——代理(Proxy)
查看>>
操作系统多进程编程、多线程编程
查看>>
fread和fseek的用法
查看>>
PHP获取网站中各文章的第一张图片的代码示例
查看>>
Python的pandas
查看>>