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

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

什么是线程:

线程指的是一条流水线的工作过程的总称

线程是CPU的基本执行单位

对比进程而言,进程仅仅是一个资源单位其包含了程序运行所需的资源,就像一个车间

而单有资源是无法生产出产品的,必须有具体的生产产品的逻辑代码

线程就相当于车间中的一条流水线,而你的代码就是流水线上的一道道工序

 

特点:

1.每个进程都会有一个默认的线程

2.每个进程可以存在多个线程

3.同一进程中的所有线程之间数据是共享的

4.创建线程的开销远比创建进程小的多

 

主线程与子线程的区别:

1.线程之间是没有父子之分,是平等的

2.主线程是由操作系统自动开启的,而子线是由程序主动开启

3.即时主线程的代码执行完毕,也不会结束进程,会等待所有线程执行完毕,进程才结束

 

 

开启线程的两种方式:

1.实例化Tread类,target参数用于指定子线程要执行的任务

from threading import  Threaddef task():    print("子线程 run........")t = Thread(target=task)t.start()print("over")  #主线程的代码执行完毕,也不会结束进程,会等待所有线程执行完毕,进程才结束

 

2.继承Tread类,覆盖run方法

from threading import  Threadclass MyThread(Thread):    def run(self):        print("子线程 run........")t = MyThread()t.start()print("over")

 

与进程在使用方法上没有任何区别,不同的是开启子线程的代码可以写在任意位置

之所以使用方法完全相同是因为,多进程其实是为了弥补多线程的缺憾而诞生的。详见GIL锁

 

线程与进程区别:

1.同一进程中 线程之间数据共享

a = 100def task():    global a    print("子线程 run........")    a = 1t = Thread(target=task)t.start()print(a) # 1print("over")

 

2.创建线程的开销远比创建进程小的多

from threading import  Threadfrom multiprocessing import  Processimport timedef task():    passif __name__ == '__main__':    start = time.time()    for i in range(100):        p = Thread(target=task)        p.start()    print(time.time()-start) # 修改Thread 为Process类 查看结果

 

3.无论开启了多少子线程PID是不会变的

from threading import  Threadimport osdef task():    print(os.getpid())for i in range(100):    p = Thread(target=task)    p.start()

 

Tread类的常用属性:

# threading模块包含的常用方法import threadingprint(threading.current_thread().name) #获取当前线程对象print(threading.active_count())     # 获取目前活跃的线程数量print(threading.enumerate())       # 获取所有线程对象t = Thread(name="aaa")# t.join()       # 主线程等待子线程执行完毕print(t.name)     # 线程名称print(t.is_alive())  # 是否存活print(t.isDaemon())  # 是否为守护线程

 

守护线程:

设置守护线程的语法与进程相同,相同的是也必须放在线程开启前设置,否则抛出异常。

守护线程的特点:

  守护线程会在被守护线程结束后立即结束

from threading import  Threadimport timedef task():    print("start......")    time.sleep(5)    print("end......")t = Thread(target=task)# t.setDaemon(True)t.daemon = Truet.start()print("main over!")

 

疑惑:

from threading import  Threadimport timedef task():    print("start....1")    time.sleep(3)    print("end......1")def task2():    print("start....2")    time.sleep(4)    print("end......2")t = Thread(target=task)t.daemon = Truet.start()t2 = Thread(target=task2)t2.start()print("main over!")

 

打印main over后主线程代码执行完毕,但是守护线程t1并没有立即结束,这是什么原因呢?

答:主线程会等待所有子线程执行完毕后结束

 

 

在上述例子中,一共有三个线程,主线程 ,t1,t2 ,  虽然t1是守护线程 ,但是t2并不是所以主线程会等待t2执行结束才结束

  顺序是:守护线程 等待 主线程 等待 其余子线程

换句话说,守护线程会在所有非守护线程执行完毕后结束。

 

 

线程锁

互斥锁

多线程的最主要特征之一是:

  同一进程中所有线程数据共享 ,  一旦共享必然出现竞争问题。

a = 10#lock = Lock()def task():    global a    #lock.acquire()    b = a - 1    time.sleep(0.1)    a = b    #lock.release()for i in  range(10):    t = Thread(target=task)    t.start()for t in threading.enumerate():    if t != threading.current_thread():        t.join()print(a)# 输出 9

当多个线程要并发修改同一资源时,也需要加互斥锁来保证数据安全。

同样的一旦加锁,就意味着串行,效率必然降低。

 

 

死锁

现有两把锁l1和l2 用于表示盘子和筷子

两个线程的目标是吃饭,要吃饭的前提是同时拿到筷子和盘子,但是两个人的目标不同一个先拿筷子 ,一个先拿盘子 , 最终造成死锁

l1 = Lock()l2 = Lock()def task():    l1.acquire()    print(threading.current_thread().name,"拿到了筷子")    time.sleep(0.1)    l2.acquire()    print(threading.current_thread().name, "拿到了盘子")    print("吃饭")    l1.release()    l2.release()def task2():    l2.acquire()    print(threading.current_thread().name, "拿到了盘子")    l1.acquire()    print(threading.current_thread().name,"拿到了筷子")    print("吃饭")    l2.release()    l1.release()t1 = Thread(target=task)t1.start()t2 = Thread(target=task2)t2.start()

共有两把锁,但是一人拿到了一把,并且互不释放,相互等待,导致程序卡死,这就死锁。

 

要发生死锁只有两种情况 : 

  1.有不止一把锁,不同线程或进程分别拿到了不同的锁不放

  2.对同一把锁执行了多次acquire

其中第二种情况我们可以通过可重入锁来解决 ! 

 

 

可重入锁

Rlock 同一个线程可以多次执行acquire,释放锁时,有几次acquire就要release几次。

但是本质上同一个线程多次执行acquire时没有任何意义的,其他线程必须等到RLock全部release之后才能访问共享资源。

所以Rlock仅仅是帮你解决了代码逻辑上的错误导致的死锁 (及锁套锁),并不能解决多个锁造成的死锁问题

# 同一把RLock 多次acquire#l1 = RLock()#l2 = l1# 不同的RLock 依然会锁死#l1 = RLock()#l2 = RLock()def task():    l1.acquire()    print(threading.current_thread().name,"拿到了筷子")    time.sleep(0.1)    l2.acquire()    print(threading.current_thread().name, "拿到了盘子")    print("吃饭")    l1.release()    l2.release()def task2():    l2.acquire()    print(threading.current_thread().name, "拿到了盘子")    l1.acquire()    print(threading.current_thread().name,"拿到了筷子")    print("吃饭")    l2.release()    l1.release()t1 = Thread(target=task)t1.start()t2 = Thread(target=task2)t2.start()

 

忠告:在处理并发安全时 用完公共资源后一定要释放锁

 

 

 

信号量  Semaphore :

信号量也是一种锁,其特殊之处在于可以让一个资源同时被多个线程共享,并控制最大的并发访问线程数量。

如果把Lock比喻为家用洗手间,同一时间只能一个人使用。

那信号量就可以看做公共卫生间,同一时间可以有多个人同时使用。

 

from threading import  Thread,Semaphore,current_threadimport times = Semaphore(3)   #设置访问数量为3def task():    s.acquire()    print("%s running........" % current_thread())    time.sleep(1)    s.release()    for i in range(20):    Thread(target=task).start()

 

 

 

转载于:https://www.cnblogs.com/HZLS/p/10987913.html

你可能感兴趣的文章
makefile编写---单个子目录编译模板
查看>>
Oracle DB_LINK如何使用
查看>>
cv resource
查看>>
关于加快INSERT语句执行速度和HINT /*+ append */及/*+ append nologging */的使用
查看>>
JDK源代码学习系列07----Stack
查看>>
firefox
查看>>
PS批处理的使用
查看>>
七天学会ASP.NET MVC (一)——深入理解ASP.NET MVC 【转】
查看>>
Quartz作业调度框架
查看>>
腾讯云下安装 nodejs + 实现 Nginx 反向代理
查看>>
js-权威指南学习笔记13
查看>>
《超级时间整理术》晨读笔记
查看>>
Spring Boot 2.0(二):Spring Boot 2.0尝鲜-动态 Banner
查看>>
Delphi IdTCPClient IdTCPServer 点对点传送文件
查看>>
Delphi中使用ActiveX的一些心得
查看>>
QT5.8.0+MSVC2015安装以及环境配置(不需要安装VS2015)
查看>>
(原創) C/C++的function prototype和header file (C/C++) (C)
查看>>
深入理解JavaScript系列(29):设计模式之装饰者模式
查看>>
程序员的罪与罚
查看>>
SQL*LOADER错误总结
查看>>