一、线程
1、能独立运行的基本单位————线程
2、进程的创建,撤销,与切换存在较大的时空开销,因此需要引进轻型进程
3、进程是资源分配的最小单位,线程是cpu调度的最小单位,每一个进程至少有一个线程
4、进程是车间,线程是车间中的每一个工人
5、进程中可以开启多个线程
(1)开启一个线程所需的时间要远远小于开启一个进程
(2)多个线程内部有自己的数据栈,数据不共享
(3)全局变量在多个线程之间是共享的
from threading import Thread
import time,os
#多线程并发
def func(n):
time.sleep(1)
print(n,os.getpid())
print('主线程:%s'%os.getpid())
for i in range(10):
t = Thread(target=func,args=(i,))
t.start()
输出结果:
主线程:17232
2 17232
1 17232
0 17232
3 17232
6 17232
7 17232
5 17232
4 17232
9 17232
8 17232
@导入的模块,文件所在位置,内置函数,代码等存在于主进程中的栈
(2)利用面向对象的方法
import time
from threading import Thread
class Mythread(Thread):
def __init__(self,arg):
super().__init__()
self.arg = arg
def run(self):
time.sleep(1)
print(self.arg)
for i in range(10):
t = Mythread(i)
t.start()
输出结果:
0
9
3
7
5
8
4
2
6
1
二、线程数据共享
from threading import Thread
import time,os
#多线程并发
def func(n):
global g
g = 0
print(g,n,os.getpid())
g = 100 #(全局的数据共享)
t_lst = []
for i in range(10):
t = Thread(target=func,args=(i,))
t.start()
t_lst.append(t)
for t in t_lst:t.join()
print(g)
输出结果:
0 0 9604
0 1 9604
0 2 9604
0 3 9604
0 4 9604
0 5 9604
0 6 9604
0 7 9604
0 8 9604
0 9 9604
0
三、全局解释器锁(GIL)
1、Cpython解释器的特性:同一个时刻只能只有一个线程访问CPU,解释器锁锁的是线程
(1)高CPU:计算类----高CPU利用率
(2)高IO:爬取200个网页,处理日志文件,qq聊天,处理web请求,读数据库,写数据库
import time
from threading import Thread
from multiprocessing import Process
def func(n):
n+1
if __name__ == '__main__':
start = time.time()
t_list = []
for i in range(100):
t = Thread(target=func,args=(i,))
t.start()
t_list.append(t)
for t in t_list:t.join()
t1 = time.time() - start
start = time.time()
p_list = []
for i in range(100):
p = Process(target=func,args=(i,))
p.start()
p_list.append(p)
for t in t_list:t.join()
t2 = time.time() - start
print(t1,t2)
输出结果:
0.019787311553955078 0.5348331928253174
四、线程里面的基础方法
import threading
def func(n):
print(n,threading.get_ident(),threading.Thread)#告诉子线程id和线程名字
threading.Thread(target=func,args=('hello',)).start()
print(threading.current_thread())#告诉主线程id和线程名字
print(threading.get_ident())#查看主线程id
print(threading.enumerate())#列出所有线程
输出结果:
hello 6912
<_MainThread(MainThread, started 8456)>
8456
[<_MainThread(MainThread, started 13760)>,]
五、复习
1、线程是进程中的执行单位
2、线程是cpu执行的最小单位
3、 线程之间资源共享
4、 线程的开启和关闭以及切换时间开销远远小于进程,线程本身可以在同一时间使用多个cpu
5、python与线程
(1)cpython解释器在解释代码过程中容易产生数据不安全的问题
(2)GIL全局锁 锁的是线程
六、守护线程
1、
import time
from threading import Thread
def func1():
while True:
print('*'*10)
time.sleep(1)
def func2():
time.sleep(5)
print('func2')
t = Thread(target=func1,)
t.daemon = True #守护线程
t.start()
t2 = Thread(target=func2,)
t2.start()
print('主线程')
输出结果:
'**********
主线程
'**********
'**********
'**********
'**********
func2
(1)守护进程随着主进程代码的执行结束而结束
(2)守护线程会在主线程结束之后等待其他子线程的结束才结束
(3)主线程会等待子线程的结束
(4)主进程在执行完自己的代码后不会立即结束,而是等待子进程结束之后回收子进程的资源
七、加锁解决数据不安全问题
1、
import time
from threading import Lock,Thread
def func(lock):
global n
lock.acquire()
temp = n
time.sleep(0.2)
n = temp - 1
lock.release()
n = 10
t_list = []
lock = Lock()
for i in range(10):
t = Thread(target=func,args=(lock,))
t.start()
t_list.append(t)
for t in t_list:t.join()
print(n)
输出结果:
0(不加锁会输出9)
2、科学家吃面(形成堵塞造成死锁)
from threading import Thread,Lock
import time
noodle_lock = 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()
Thread(target=eat1,args=('long',)).start()
Thread(target=eat2,args=('youxiu',)).start()
Thread(target=eat1,args=('geng',)).start()
Thread(target=eat2,args=('heihei',)).start()
输出结果:
long拿到面条
long拿到叉子了
long吃面
youxiu拿到叉子了
geng拿到面条
3、递归锁(解决死锁现象)
from threading import RLock,Thread
import time
noodle_lock = fork_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()
Thread(target=eat1,args=('long',)).start()
Thread(target=eat2,args=('youxiu',)).start()
Thread(target=eat1,args=('geng',)).start()
Thread(target=eat2,args=('yue',)).start()
输出结果:
long拿到面条
long拿到叉子了
long吃面
youxiu拿到叉子了
youxiu拿到面条
youxiu吃面
geng拿到面条
geng拿到叉子了
geng吃面
yue拿到叉子了
yue拿到面条
yue吃面
@在同一个线程或同一个进程中出现两把或两把以上的锁的时候会出现死锁