线程 ----- 守护线程 ----死锁 ----递归锁 ---线程队列

之前已经了解了进程的概念,程序并不能单独运行,只有将程序装载到内存中,系统为它分配资源才能运行

而这种执行的程序称为进程

程序和进程的区别在于:程序是指令的集合,它是进程运行的静态描述文本  

为什么要有线程


进程有很多优点,它提供了多道编程,让我们感觉每个人都拥有自己的cpu和其他资源,可以提高计算机的利用率

进程只能在一个时间干一件事情,如果想干两件事情或多件事,进程就无能为力了。

进程在执行的过程中如果阻塞,例如等待输入,整个过程就会挂起,即使进程中有些工作不依赖输入的数据,也将

无法执行

进程和线程的关系

地址空间和其他资源(如打开文件):进程间相互独立,同一进程的各线程间共享。某进程内的线程在其他进程不可见

通信:进程间通信IPC,线程间可以直接读写进程数据段(如全局变量)来进行通信----需要进程同步和互斥手段的辅助,以保证数据的一致性。

需要进程同步和互斥手段的辅助,以保证数据的一致性。

在多线程操作系统中,进程不是一个可执行的实体。

线程的特点

在多线程的操作系统中,通常是在一个进程中包括多个线程,每个线程都是作为利用CPU的基本单位,是花费最小开销的实体。线程具有以下属性。

  1)轻型实体
  线程中的实体基本上不拥有系统资源,只是有一点必不可少的、能保证独立运行的资源。
  线程的实体包括程序、数据和TCB。线程是动态概念,它的动态特性由线程控制块TCB(Thread Control Block)描述。
TCB包括以下信息:
(1)线程状态。
(2)当线程不运行时,被保存的现场资源。
(3)一组执行堆栈。
(4)存放每个线程的局部变量主存区。
(5)访问同一个进程中的主存和其它资源。
用于指示被执行指令序列的程序计数器、保留局部变量、少数状态参数和返回地址等的一组寄存器和堆栈。
TCB信息
2)独立调度和分派的基本单位。
  在多线程OS中,线程是能独立运行的基本单位,因而也是独立调度和分派的基本单位。由于线程很“轻”,故线程的切换非常迅速且开销小(在同一进程中的)。
  3)共享进程资源。
  线程在同一进程中的各个线程,都可以共享该进程所拥有的资源,这首先表现在:所有线程都具有相同的进程id,这意味着,线程可以访问该进程的每一个内存资源;此外,还可以访问进程所拥有的已打开文件、定时器、信号量机构等。由于同一个进程内的线程共享内存和文件,所以线程之间互相通信不必调用内核。
  4)可并发执行。
  在一个进程中的多个线程之间,可以并发执行,甚至允许在一个进程中所有线程都能并发执行;同样,不同进程中的线程也能并发执行,充分利用和发挥了处理机与外围设备并行工作的能力。

全局解释器锁GIL

  Python代码的执行由Python虚拟机(也叫解释器主循环)来控制。Python在设计之初就考虑到要在主循环中,同时只有一个线程在执行。虽然 Python 解释器中可以“运行”多个线程,但在任意时刻只有一个线程在解释器中运行。
  对Python虚拟机的访问由全局解释器锁(GIL)来控制,正是这个锁能保证同一时刻只有一个线程在运行。

  在多线程环境中,Python 虚拟机按以下方式执行:

  a、设置 GIL;

  b、切换到一个线程去运行;

  c、运行指定数量的字节码指令或者线程主动让出控制(可以调用 time.sleep(0));

  d、把线程设置为睡眠状态;

  e、解锁 GIL;

  d、再次重复以上所有步骤。
  在调用外部代码(如 C/C++扩展函数)的时候,GIL将会被锁定,直到这个函数结束为止(由于在这期间没有Python的字节码被运行,所以不会做线程切换)编写扩展的程序员可以主动解锁GIL。

python线程模块的选择

 Python提供了几个用于多线程编程的模块,包括thread、threading和Queue等。thread和threading模块允许程序员创建和管理线程。thread模块提供了基本的线程和锁的支持,threading提供了更高级别、功能更强的线程管理的功能。Queue模块允许用户创建一个可以用于多个线程之间共享数据的队列数据结构。
  避免使用thread模块,因为更高级别的threading模块更为先进,对线程的支持更为完善,而且使用thread模块里的属性有可能会与threading出现冲突;其次低级别的thread模块的同步原语很少(实际上只有一个),而threading模块则有很多;再者,thread模块中当主线程结束时,所有的线程都会被强制结束掉,没有警告也不会有正常的清除工作,至少threading模块能确保重要的子线程退出后进程才退出。 

  thread模块不支持守护线程,当主线程退出时,所有的子线程不论它们是否还在工作,都会被强行退出。而threading模块支持守护线程,守护线程一般是一个等待客户请求的服务器,如果没有客户提出请求它就在那等着,如果设定一个线程为守护线程,就表示这个线程是不重要的,在进程退出的时候,不用等待这个线程退出。

threading模块

multiprocess模块的完全模仿了threading模块的接口,二者在使用层面,有很大的相似性,因而不再详细介绍

线程的创建

vfrom threading import Thread
import time
import os
def sayhi(name):
    time.sleep(2)
    print('%s say hello'%name,os.getpid())

if __name__ == '__main__':
    t=Thread(target=sayhi,args=('egon',))
    t.start()
    print('主线程',os.getpid())
线程创建1
from threading import Thread
import time
class Sayhi(Thread):
    def __init__(self,name):
        super().__init__()
        self.name=name
    def run(self):
        time.sleep(2)
        print('%s say hello' % self.name)


if __name__ == '__main__':
    t = Sayhi('egon')
    t.start()
    print('主线程')
线程创建2

多线程和多进程的比较

from threading import Thread
from multiprocessing import Process
import os

def work():
    print('hello',os.getpid())

if __name__ == '__main__':
    # part1:在主进程下开启多个线程,每个线程都跟主进程的pid一样
    t1=Thread(target=work)
    t2=Thread(target=work)
    t1.start()
    t2.start()
    print('主线程/主进程',os.getpid())
    # part2:开多个进程,每个进程都有不同的pid
    p1=Process(target=work)
    p2=Process(target=work)
    p1.start()
    p2.start()
    print('主线程/主进程',os.getpid())

'''
hello 8936
hello 8936
主线程/主进程 8936
主线程/主进程 8936
hello 12400
hello 19004
'''
pid比较
from threading import Thread
from multiprocessing import Process
import os

def work():
    print('hello')


if __name__ == '__main__':
    # 在主进程下开启线程
    t=Thread(target=work)
    t.start()
    print('主线程/主进程')
    print('-----------------------')

    # 在主进程下开启子进程
    t=Process(target=work)
    t.start()
    print('主线程/主进程')
'''
hello
主线程/主进程
-----------------------
主线程/主进程
hello
'''
开启效率比较
vfrom threading import Thread
from multiprocessing import Process
import os

def work():
    global n
    n=0

if __name__ == '__main__':
    # n=100
    # p=Process(target=work)
    # p.start()
    # p.join()
    # print('主',n) #主 100

    n=100
    t=Thread(target=work)
    t.start()
    t.join()
    print('',n) #主 0
内存数据共享问题

练习:多线程实现socket

import multiprocessing
import threading

import socket
s=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
s.bind(('127.0.0.1',8080))
s.listen(5)

def action(conn):
    while True:
        data=conn.recv(1024)
        print(data)
        conn.send(data.upper())

if __name__ == '__main__':

    while True:
        conn,addr=s.accept()


        p=threading.Thread(target=action,args=(conn,))
        p.start()
server端
import socket

s=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
s.connect(('127.0.0.1',8080))

while True:
    msg=input('>>: ').strip()
    if not msg:continue

    s.send(msg.encode('utf-8'))
    data=s.recv(1024)
    print(data)
client

Thread类的其他方法

Thread实例对象的方法
  # isAlive(): 返回线程是否活动的。
  # getName(): 返回线程名。
  # setName(): 设置线程名。

threading模块提供的一些方法:
  # threading.currentThread(): 返回当前的线程变量。
  # threading.enumerate(): 返回一个包含正在运行的线程的list。正在运行指线程启动后、结束前,不包括启动前和终止后的线程。
  # threading.activeCount(): 返回正在运行的线程数量,与len(threading.enumerate())有相同的结果。
View Code

例子:

from threading import Thread
import threading
from multiprocessing import Process
import os

def work():
    import time
    time.sleep(3)
    print(threading.current_thread().getName()) #Thread-1

if __name__ == '__main__':
    t=Thread(target=work)
    t.start()

    print(threading.current_thread().getName()) #MainThread
    print(threading.current_thread()) # <_MainThread(MainThread, started 11068)>
    print(threading.enumerate()) #[<_MainThread(MainThread, started 11068)>, <Thread(Thread-1, started 11436)>]
    print(threading.active_count()) # 2
    print('主线程/主进程')
用法使用
from threading import Thread
import time
def sayhi(name):
    time.sleep(2)
    print('%s say hello'%name)

if __name__ == '__main__':
    t=Thread(target=sayhi,args=('egon',))
    t.start()
    # t.join()
    print('主线程')
    print(t.is_alive())

'''
有t.join()的情况
egon say hello
主线程
False

没有t.join()的情况
主线程
True
egon say hello
'''
join情况

守护线程

无论是进程还是线程,都遵循:守护xx会等待主xx运行完毕后被销毁。需要强调的是:运行完毕并非终止运行

#1.对主进程来说,运行完毕指的是主进程代码运行完毕
#2.对主线程来说,运行完毕指的是主线程所在的进程内所有非守护线程统统运行完毕,主线程才算运行完毕
#1 主进程在其代码结束后就已经算运行完毕了(守护进程在此时就被回收),然后主进程会一直等非守护的子进程都运行完毕后回收子进程的资源(否则会产生僵尸进程),才会结束,
#2 主线程在其他非守护线程运行完毕后才算运行完毕(守护线程在此时就被回收)。因为主线程的结束意味着进程的结束,进程整体的资源都将被回收,而进程必须保证非守护线程都运行完毕后才能结束。
from threading import Thread
import time
def sayhi(name):
    time.sleep(2)
    print('%s say hello' %name)

if __name__ == '__main__':
    t=Thread(target=sayhi,args=('egon',))
    t.setDaemon(True) #必须在t.start()之前设置
    t.start()

    print('主线程')
    print(t.is_alive())
'''
主线程
True
#由于t设置为守护线程,所以等到主线程完毕了,子线程来不及打印就结束了
'''
守护线程1
from threading import Thread
import time
def foo():
    print(123)
    time.sleep(1)
    print("end123")

def bar():
    print(456)
    time.sleep(3)
    print("end456")

t1=Thread(target=bar)
t2=Thread(target=foo)

t1.daemon=True
t1.start()
t2.start()
print("main-------")

'''
456
123
main-------
end123
'''
守护线程2

线程的执行,是异步的,顺序随机

import os
import time
from threading import Thread


def func(i):
    print('--->子线程', os.getpid())
    time.sleep(1)
    print('*' * i)


if __name__ == '__main__':
    print('主进程', os.getpid())
    start = time.time()
    thread_lst = []
    for i in range(10):
        t = Thread(target=func, args=(i,))
        t.start()
        # Thread.name 获取和设置线程的名称
        # Thread.ident 获取线程的标识符
        print('-->', t.name, t.ident)
        thread_lst.append(t)
    for t in thread_lst: t.join()  # 等待所有线程结束
    print(time.time() - start)

'''
主进程 10816
--->子线程 10816
--> Thread-1 16904
--->子线程 10816
--> Thread-2 3112
--->子线程 10816
--> Thread-3 16256
--->子线程 10816
--> Thread-4 960
--->子线程 10816
--> Thread-5 896
--->子线程 10816
--> Thread-6 18740
--->子线程 10816
--> Thread-7 17364
--->子线程 10816
--> Thread-8 15868
--->子线程 10816
--> Thread-9 5948
--->子线程 10816
--> Thread-10 9356
******
**
*********
*
***
********
*******
****
*****

1.0063774585723877
'''
View Code

多个线程或者多个进程操作同一个数据,容易造成数据不安全,所以需要加把锁

GIL锁的是线程,不能锁住内存中的数据,对于全局变量的修改必须要枷锁,为了不影响效率,不是共享的数据不用设置成全局变量

资源的抢占例子一:sleep的值为0.1的情况

from threading import Thread
import os,time
def work():
    global n
    temp=n
    print('---','temp:%s ,n:%s'%(id(temp),id(n)))
    print(temp)
    time.sleep(0.1) # 0.01 0.001 0.0001
    n=temp-1
if __name__ == '__main__':
    n=100
    l=[]
    for i in range(100):
        p=Thread(target=work)
        l.append(p)
        p.start()
    for p in l:
        p.join()
    print('----主-----',n)
View Code

改变sleep的值,得到的结果显示如下

--- temp:1961589312 ,n:1961589312
100
--- temp:1961589312 ,n:1961589312
100
--- temp:1961589312 ,n:1961589312
100
--- temp:1961589312 ,n:1961589312
100
--- temp:1961589312 ,n:1961589312
100
--- temp:1961589312 ,n:1961589312
100
--- temp:1961589312 ,n:1961589312
100
--- temp:1961589312 ,n:1961589312
100
--- temp:1961589312 ,n:1961589312
100
--- temp:1961589312 ,n:1961589312
100
--- temp:1961589312 ,n:1961589312
100
--- temp:1961589312 ,n:1961589312
100
--- temp:1961589312 ,n:1961589312
100
--- temp:1961589312 ,n:1961589312
100
--- temp:1961589312 ,n:1961589312
100
--- temp:1961589312 ,n:1961589312
100
--- temp:1961589312 ,n:1961589312
100
--- temp:1961589312 ,n:1961589312
100
--- temp:1961589312 ,n:1961589312
100
--- temp:1961589312 ,n:1961589312
100
--- temp:1961589312 ,n:1961589312
100
--- temp:1961589312 ,n:1961589312
100
--- temp:1961589312 ,n:1961589312
100
--- temp:1961589312 ,n:1961589312
100
--- temp:1961589312 ,n:1961589312
100
--- temp:1961589312 ,n:1961589312
100
--- temp:1961589312 ,n:1961589312
100
--- temp:1961589312 ,n:1961589312
100
--- temp:1961589312 ,n:1961589312
100
--- temp:1961589312 ,n:1961589312
100
--- temp:1961589312 ,n:1961589312
100
--- temp:1961589312 ,n:1961589312
100
--- temp:1961589312 ,n:1961589312
100
--- temp:1961589312 ,n:1961589312
100
--- temp:1961589312 ,n:1961589312
100
--- temp:1961589312 ,n:1961589312
100
--- temp:1961589312 ,n:1961589312
100
--- temp:1961589312 ,n:1961589312
100
--- temp:1961589312 ,n:1961589312
100
--- temp:1961589312 ,n:1961589312
100
--- temp:1961589312 ,n:1961589312
100
--- temp:1961589312 ,n:1961589312
100
--- temp:1961589312 ,n:1961589312
100
--- temp:1961589312 ,n:1961589312
100
--- temp:1961589312 ,n:1961589312
100
--- temp:1961589312 ,n:1961589312
100
--- temp:1961589312 ,n:1961589312
100
--- temp:1961589312 ,n:1961589312
100
--- temp:1961589312 ,n:1961589312
100
--- temp:1961589312 ,n:1961589312
100
--- temp:1961589312 ,n:1961589312
100
--- temp:1961589312 ,n:1961589312
100
--- temp:1961589312 ,n:1961589312
100
--- temp:1961589312 ,n:1961589312
100
--- temp:1961589312 ,n:1961589312
100
--- temp:1961589312 ,n:1961589312
100
--- temp:1961589312 ,n:1961589312
100
--- temp:1961589312 ,n:1961589312
100
--- temp:1961589312 ,n:1961589312
100
--- temp:1961589312 ,n:1961589312
100
--- temp:1961589312 ,n:1961589312
100
--- temp:1961589312 ,n:1961589312
100
--- temp:1961589312 ,n:1961589312
100
--- temp:1961589312 ,n:1961589312
100
--- temp:1961589312 ,n:1961589312
100
--- temp:1961589312 ,n:1961589312
100
--- temp:1961589312 ,n:1961589312
100
--- temp:1961589312 ,n:1961589312
100
--- temp:1961589312 ,n:1961589312
100
--- temp:1961589312 ,n:1961589312
100
--- temp:1961589312 ,n:1961589312
100
--- temp:1961589312 ,n:1961589312
100
--- temp:1961589312 ,n:1961589312
100
--- temp:1961589312 ,n:1961589312
100
--- temp:1961589312 ,n:1961589312
100
--- temp:1961589312 ,n:1961589312
100
--- temp:1961589312 ,n:1961589312
100
--- temp:1961589312 ,n:1961589312
100
--- temp:1961589312 ,n:1961589312
100
--- temp:1961589312 ,n:1961589312
100
--- temp:1961589312 ,n:1961589312
100
--- temp:1961589312 ,n:1961589312
100
--- temp:1961589312 ,n:1961589312
100
--- temp:1961589312 ,n:1961589312
100
--- temp:1961589312 ,n:1961589312
100
--- temp:1961589312 ,n:1961589312
100
--- temp:1961589312 ,n:1961589312
100
--- temp:1961589312 ,n:1961589312
100
--- temp:1961589312 ,n:1961589312
100
--- temp:1961589312 ,n:1961589312
100
--- temp:1961589312 ,n:1961589312
100
--- temp:1961589312 ,n:1961589312
100
--- temp:1961589312 ,n:1961589312
100
--- temp:1961589312 ,n:1961589312
100
--- temp:1961589312 ,n:1961589312
100
--- temp:1961589312 ,n:1961589312
100
--- temp:1961589312 ,n:1961589312
100
--- temp:1961589312 ,n:1961589312
100
--- temp:1961589312 ,n:1961589312
100
--- temp:1961589312 ,n:1961589312
100
----主----- 99
sleep(0.1)
--- temp:1961589312 ,n:1961589312
100
--- temp:1961589312 ,n:1961589312
100
--- temp:1961589312 ,n:1961589312
100
--- temp:1961589312 ,n:1961589312
100
--- temp:1961589312 ,n:1961589312
100
--- temp:1961589312 ,n:1961589312
100
--- temp:1961589312 ,n:1961589312
100
--- temp:1961589280 ,n:1961589280
99
--- temp:1961589280 ,n:1961589280
99
--- temp:1961589280 ,n:1961589280
99
--- temp:1961589280 ,n:1961589280
99
--- temp:1961589280 ,n:1961589280
99
--- temp:1961589280 ,n:1961589280
99
--- temp:1961589280 ,n:1961589280
99
--- temp:1961589248 ,n:1961589248
98
--- temp:1961589248 ,n:1961589248
98
--- temp:1961589248 ,n:1961589248
98
--- temp:1961589248 ,n:1961589248
98
--- temp:1961589248 ,n:1961589248
98
--- temp:1961589216 ,n:1961589216
97
--- temp:1961589216 ,n:1961589216
97
--- temp:1961589216 ,n:1961589216
97
--- temp:1961589216 ,n:1961589216
97
--- temp:1961589216 ,n:1961589216
--- temp:1961589216 ,n:1961589216
97
97
--- temp:1961589184 ,n:1961589184
96
--- temp:1961589184 ,n:1961589184
96
--- temp:1961589184 ,n:1961589184
96
--- temp:1961589184 ,n:1961589184
96
--- temp:1961589184 ,n:1961589184
--- temp:1961589184 ,n:1961589184
96
96
--- temp:1961589152 ,n:1961589152
95
--- temp:1961589152 ,n:1961589152
95
--- temp:1961589152 ,n:1961589152
95
--- temp:1961589152 ,n:1961589152
95
--- temp:1961589152 ,n:1961589152
95
--- temp:1961589152 ,n:1961589152
95
--- temp:1961589152 ,n:1961589152
95
--- temp:1961589152 ,n:1961589152
95
--- temp:1961589152 ,n:1961589152
95
--- temp:1961589120 ,n:1961589120
94
--- temp:1961589120 ,n:1961589120
94
--- temp:1961589120 ,n:1961589120
94
--- temp:1961589120 ,n:1961589120
94
--- temp:1961589120 ,n:1961589120
94
--- temp:1961589120 ,n:1961589120
94
--- temp:1961589120 ,n:1961589120
94
--- temp:1961589120 ,n:1961589120
94
--- temp:1961589120 ,n:1961589120
94
--- temp:1961589088 ,n:1961589088
93
--- temp:1961589088 ,n:1961589088
93
--- temp:1961589088 ,n:1961589088
93
--- temp:1961589088 ,n:1961589088
93
--- temp:1961589088 ,n:1961589088
93
--- temp:1961589088 ,n:1961589088
93
--- temp:1961589088 ,n:1961589088
93
--- temp:1961589088 ,n:1961589088
93
--- temp:1961589088 ,n:1961589088
93
--- temp:1961589088 ,n:1961589088
93
--- temp:1961589056 ,n:1961589056
92
--- temp:1961589056 ,n:1961589056
92
--- temp:1961589056 ,n:1961589056
92
--- temp:1961589056 ,n:1961589056
92
--- temp:1961589056 ,n:1961589056
92
--- temp:1961589056 ,n:1961589056
92
--- temp:1961589056 ,n:1961589056
92
--- temp:1961589056 ,n:1961589056
92
--- temp:1961589056 ,n:1961589056
92
--- temp:1961589056 ,n:1961589056
92
--- temp:1961589056 ,n:1961589056
92
--- temp:1961589024 ,n:1961589024
91
--- temp:1961589024 ,n:1961589024
91
--- temp:1961589024 ,n:1961589024
91
--- temp:1961589024 ,n:1961589024
91
--- temp:1961589024 ,n:1961589024
91
--- temp:1961589024 ,n:1961589024
91
--- temp:1961589024 ,n:1961589024
91
--- temp:1961589024 ,n:1961589024
91
--- temp:1961588992 ,n:1961588992
90
--- temp:1961588992 ,n:1961588992
90
--- temp:1961588992 ,n:1961588992
90
--- temp:1961588992 ,n:1961588992
--- temp:1961588992 ,n:1961588992
90
90
--- temp:1961588992 ,n:1961588992
90
--- temp:1961588992 ,n:1961588992
90
--- temp:1961588992 ,n:1961588992
90
--- temp:1961588992 ,n:1961588992
90
--- temp:1961588992 ,n:1961588992
90
--- temp:1961588992 ,n:1961588992
90
--- temp:1961588992 ,n:1961588992
90
--- temp:1961588960 ,n:1961588960
89
--- temp:1961588960 ,n:1961588960
89
--- temp:1961588960 ,n:1961588960
89
--- temp:1961588960 ,n:1961588960
89
--- temp:1961588960 ,n:1961588960
89
--- temp:1961588960 ,n:1961588960
89
--- temp:1961588960 ,n:1961588960
89
--- temp:1961588960 ,n:1961588960
89
--- temp:1961588960 ,n:1961588960
89
--- temp:1961588960 ,n:1961588960
89
----主----- 88
sleep(0.001)
--- temp:1961589312 ,n:1961589312
100
--- temp:1961589312 ,n:1961589312
100
--- temp:1961589312 ,n:1961589312
100
--- temp:1961589312 ,n:1961589312
100
--- temp:1961589312 ,n:1961589312
100
--- temp:1961589312 ,n:1961589312
100
--- temp:1961589280 ,n:1961589280
99
--- temp:1961589280 ,n:1961589280
99
--- temp:1961589280 ,n:1961589280
99
--- temp:1961589280 ,n:1961589280
99
--- temp:1961589280 ,n:1961589280
99
--- temp:1961589280 ,n:1961589280
99
--- temp:1961589248 ,n:1961589248
98
--- temp:1961589248 ,n:1961589248
98
--- temp:1961589248 ,n:1961589248
98
--- temp:1961589248 ,n:1961589248
98
--- temp:1961589248 ,n:1961589248
98
--- temp:1961589248 ,n:1961589248
98
--- temp:1961589248 ,n:1961589248
98
--- temp:1961589216 ,n:1961589216
97
--- temp:1961589216 ,n:1961589216
97
--- temp:1961589216 ,n:1961589216
97
--- temp:1961589216 ,n:1961589216
97
--- temp:1961589216 ,n:1961589216
97
--- temp:1961589216 ,n:1961589216
97
--- temp:1961589216 ,n:1961589216
97
--- temp:1961589216 ,n:1961589216
97
--- temp:1961589184 ,n:1961589184
96
--- temp:1961589216 ,n:1961589216
97
--- temp:1961589216 ,n:1961589216
97
--- temp:1961589216 ,n:1961589216
97
--- temp:1961589184 ,n:1961589184
96
--- temp:1961589184 ,n:1961589184
96
--- temp:1961589184 ,n:1961589184
96
--- temp:1961589184 ,n:1961589184
96
--- temp:1961589184 ,n:1961589184
96
--- temp:1961589184 ,n:1961589184
96
--- temp:1961589184 ,n:1961589184
96
--- temp:1961589184 ,n:1961589184
96
--- temp:1961589184 ,n:1961589184
96
--- temp:1961589152 ,n:1961589152
95
--- temp:1961589152 ,n:1961589152
95
--- temp:1961589152 ,n:1961589152
95
--- temp:1961589152 ,n:1961589152
95
--- temp:1961589152 ,n:1961589152
95
--- temp:1961589152 ,n:1961589152
95
--- temp:1961589152 ,n:1961589152
95
--- temp:1961589152 ,n:1961589152
95
--- temp:1961589152 ,n:1961589152
95
--- temp:1961589120 ,n:1961589120
94
--- temp:1961589120 ,n:1961589120
94
--- temp:1961589120 ,n:1961589120
94
--- temp:1961589120 ,n:1961589120
94
--- temp:1961589120 ,n:1961589120
94
--- temp:1961589120 ,n:1961589120
94
--- temp:1961589120 ,n:1961589120
94
--- temp:1961589120 ,n:1961589120
94
--- temp:1961589120 ,n:1961589120
94
--- temp:1961589088 ,n:1961589088
93
--- temp:1961589088 ,n:1961589088
93
--- temp:1961589088 ,n:1961589088
93
--- temp:1961589088 ,n:1961589088
93
--- temp:1961589088 ,n:1961589088
93
--- temp:1961589088 ,n:1961589088
93
--- temp:1961589088 ,n:1961589088
93
--- temp:1961589088 ,n:1961589088
93
--- temp:1961589088 ,n:1961589088
93
--- temp:1961589088 ,n:1961589088
93
--- temp:1961589088 ,n:1961589088
93
--- temp:1961589088 ,n:1961589088
93
--- temp:1961589056 ,n:1961589056
92
--- temp:1961589056 ,n:1961589056
92
--- temp:1961589056 ,n:1961589056
92
--- temp:1961589056 ,n:1961589056
92
--- temp:1961589056 ,n:1961589056
92
--- temp:1961589056 ,n:1961589056
92
--- temp:1961589056 ,n:1961589056
92
--- temp:1961589056 ,n:1961589056
92
--- temp:1961589056 ,n:1961589056
92
--- temp:1961589056 ,n:1961589056
92
--- temp:1961589056 ,n:1961589056
92
--- temp:1961589056 ,n:1961589056
92
--- temp:1961589024 ,n:1961589024
91
--- temp:1961589024 ,n:1961589024
91
--- temp:1961589024 ,n:1961589024
91
--- temp:1961589024 ,n:1961589024
91
--- temp:1961589024 ,n:1961589024
91
--- temp:1961589024 ,n:1961589024
91
--- temp:1961589024 ,n:1961589024
91
--- temp:1961589024 ,n:1961589024
91
--- temp:1961589024 ,n:1961589024
91
--- temp:1961589024 ,n:1961589024
91
--- temp:1961588992 ,n:1961588992
90
--- temp:1961588992 ,n:1961588992
90
--- temp:1961588992 ,n:1961588992
90
--- temp:1961588992 ,n:1961588992
90
--- temp:1961588992 ,n:1961588992
90
--- temp:1961588992 ,n:1961588992
90
--- temp:1961588992 ,n:1961588992
90
--- temp:1961588992 ,n:1961588992
90
----主----- 89
sleep(0.0001)

加锁的情况

分析:
  #1.100个线程去抢GIL锁,即抢执行权限
     #2. 肯定有一个线程先抢到GIL(暂且称为线程1),然后开始执行,一旦执行就会拿到lock.acquire()
     #3. 极有可能线程1还未运行完毕,就有另外一个线程2抢到GIL,然后开始运行,但线程2发现互斥锁lock还未被线程1释放,于是阻塞,被迫交出执行权限,即释放GIL
    #4.直到线程1重新抢到GIL,开始从上次暂停的位置继续执行,直到正常释放互斥锁lock,然后其他的线程再重复2 3 4的过程
GIL锁和互斥锁分析
from threading import Thread,Lock
import os,time
def work():
    global n
    lock.acquire()
    temp=n
    time.sleep(0.1) #此处的sleep无论取什么值,或者注销加锁都是一样的值
    n=temp-1
    lock.release()
if __name__ == '__main__':
    lock=Lock()
    n=100
    l=[]
    for i in range(100):
        p=Thread(target=work)
        l.append(p)
        p.start()
    for p in l:
        p.join()

    print(n) #结果肯定为0,由原来的并发执行变成串行,牺牲了执行效率保证了数据安全
View Code
#不加锁:并发执行,速度快,数据不安全
from threading import current_thread,Thread,Lock
import os,time
def task():
    global n
    print('%s is running' %current_thread().getName())
    temp=n
    time.sleep(0.5)
    n=temp-1


if __name__ == '__main__':
    n=100
    lock=Lock()
    threads=[]
    start_time=time.time()
    for i in range(100):
        t=Thread(target=task)
        threads.append(t)
        t.start()
    for t in threads:
        t.join()

    stop_time=time.time()
    print('主:%s n:%s' %(stop_time-start_time,n))

'''
Thread-1 is running
Thread-2 is running
......
Thread-100 is running
主:0.5216062068939209 n:99
'''


#不加锁:未加锁部分并发执行,加锁部分串行执行,速度慢,数据安全
from threading import current_thread,Thread,Lock
import os,time
def task():
    #未加锁的代码并发运行
    time.sleep(3)
    print('%s start to run' %current_thread().getName())
    global n
    #加锁的代码串行运行
    lock.acquire()
    temp=n
    time.sleep(0.5)
    n=temp-1
    lock.release()

if __name__ == '__main__':
    n=100
    lock=Lock()
    threads=[]
    start_time=time.time()
    for i in range(100):
        t=Thread(target=task)
        threads.append(t)
        t.start()
    for t in threads:
        t.join()
    stop_time=time.time()
    print('主:%s n:%s' %(stop_time-start_time,n))

'''
Thread-1 is running
Thread-2 is running
......
Thread-100 is running
主:53.294203758239746 n:0
'''

#有的同学可能有疑问:既然加锁会让运行变成串行,那么我在start之后立即使用join,就不用加锁了啊,也是串行的效果啊
#没错:在start之后立刻使用jion,肯定会将100个任务的执行变成串行,毫无疑问,最终n的结果也肯定是0,是安全的,但问题是
#start后立即join:任务内的所有代码都是串行执行的,而加锁,只是加锁的部分即修改共享数据的部分是串行的
#单从保证数据安全方面,二者都可以实现,但很明显是加锁的效率更高.
from threading import current_thread,Thread,Lock
import os,time
def task():
    time.sleep(3)
    print('%s start to run' %current_thread().getName())
    global n
    temp=n
    time.sleep(0.5)
    n=temp-1


if __name__ == '__main__':
    n=100
    lock=Lock()
    start_time=time.time()
    for i in range(100):
        t=Thread(target=task)
        t.start()
        t.join()
    stop_time=time.time()
    print('主:%s n:%s' %(stop_time-start_time,n))

'''
Thread-1 start to run
Thread-2 start to run
......
Thread-100 start to run
主:350.6937336921692 n:0 #耗时是多么的恐怖
'''
互斥锁和join的区别

 加锁不当也会出现死锁的情况

import time
from threading import Lock,Thread
noodle = 100
fork = 100
noodle_lock = Lock()
fork_lock = Lock()

def eat1(name):
    global noodle,fork
    noodle_lock.acquire()
    print('%s拿到面了' % name)
    fork_lock.acquire()
    print('%s拿到叉子了' % name)
    noodle -= 1
    print('%s吃面'%name)
    time.sleep(0.1)
    fork_lock.release()
    print('%s放下叉子了' % name)
    noodle_lock.release()
    print('%s放下面' % name)

def eat2(name):
    global noodle,fork
    fork_lock.acquire()
    print('%s拿到叉子了'%name)
    noodle_lock.acquire()
    print('%s拿到面了'%name)
    noodle -= 1
    print('%s吃面'%name)
    time.sleep(0.1)
    noodle_lock.release()
    print('%s放下面'%name)
    fork_lock.release()
    print('%s放下叉子了'%name)


for i in ['alex','wusir','egon','快老师']:
    Thread(target=eat1,args=(i,)).start()
    Thread(target=eat2,args=(i+'2',)).start()

#结果夯住
死锁

进而引入了递归锁

# 递归锁的特点
# 如果能够在第一个acquire的地方通过
# 那么在一个线程中后面所有的acquire都能通过
# 但是其他所有的线程都会在第一个acquire处阻塞
# 在这个线程中acquire了多少次,就必须release多少次
# 如果acquire的次数和release的次数不想等,那么其他线程也不能继续向下执行

典型例子:科学家吃面

import time
from threading import Thread,Lock
noodle_lock = Lock()
fork_lock = Lock()
def eat1(name):
    noodle_lock.acquire()
    print('%s 抢到了面条'%name)
    fork_lock.acquire()
    print('%s 抢到了叉子'%name)
    print('%s 吃面'%name)
    fork_lock.release()
    noodle_lock.release()

def eat2(name):
    fork_lock.acquire()
    print('%s 抢到了叉子' % name)
    time.sleep(1)
    noodle_lock.acquire()
    print('%s 抢到了面条' % name)
    print('%s 吃面' % name)
    noodle_lock.release()
    fork_lock.release()

for name in ['哪吒','egon','yuan']:
    t1 = Thread(target=eat1,args=(name,))
    t2 = Thread(target=eat2,args=(name,))
    t1.start()
    t2.start()
死锁问题
import time
from threading import Thread,RLock
fork_lock = noodle_lock = RLock()
def eat1(name):
    noodle_lock.acquire()
    print('%s 抢到了面条'%name)
    fork_lock.acquire()
    print('%s 抢到了叉子'%name)
    print('%s 吃面'%name)
    fork_lock.release()
    noodle_lock.release()

def eat2(name):
    fork_lock.acquire()
    print('%s 抢到了叉子' % name)
    time.sleep(1)
    noodle_lock.acquire()
    print('%s 抢到了面条' % name)
    print('%s 吃面' % name)
    noodle_lock.release()
    fork_lock.release()

for name in ['哪吒','egon','yuan']:
    t1 = Thread(target=eat1,args=(name,))
    t2 = Thread(target=eat2,args=(name,))
    t1.start()
    t2.start()
递归锁解决死锁问题

 线程队列   ----queue

queue是线程的队列,用法和进程的Queue类似

1 queue.Queue  先进先出

import queue

q=queue.Queue()
q.put('first')
q.put('second')
q.put('third')

print(q.get())
print(q.get())
print(q.get())
'''
结果(先进先出):
first
second
third
'''
View Code

2  queue.LifoQueue 后进先出

import queue

q=queue.LifoQueue()
q.put('first')
q.put('second')
q.put('third')

print(q.get())
print(q.get())
print(q.get())
'''
结果(后进先出):
third
second
first
'''
View Code

3 queue.PriorityQueue 优先级队列,数字越小,优先级越高,越先取出

import queue

q=queue.PriorityQueue()
#put进入一个元组,元组的第一个元素是优先级(通常是数字,也可以是非数字之间的比较),数字越小优先级越高
q.put((20,'a'))
q.put((10,'b'))
q.put((30,'c'))

print(q.get())
print(q.get())
print(q.get())
'''
结果(数字越小优先级越高,优先级高的优先出队):
(10, 'b')
(20, 'a')
(30, 'c')
'''
View Code
原文地址:https://www.cnblogs.com/mmyy-blog/p/9435897.html