进程和线程
进程是执行中的计算机程序。每个进程都拥有自己的地址空间、内存、数据栈及其它的辅助数据。操作系统管理着所有的进程,并为这些进程合理分配时间。进程可以通过派生新的进程来执行其它任务,不过每个进程都拥有自己的内存和数据栈等,进程之间的数据交换采用 进程间通信(IPC) 方式。
线程在进程之下执行,一个进程下可以运行多个线程,它们之间共享相同上下文。线程包括开始、执行顺序和结束三部分。它有一个指针,用于记录当前运行的上下文。当其它线程执行时,它可以被抢占(中断)和临时挂起(也称睡眠) ——这种做法叫做 让步(yielding)。
一个进程中的各个线程与主进程共享同一片数据空间,与独立进程相比,线程之间信息共享和通信更加容易。线程一般以并发执行,正是由于这种并发和数据共享机制,使多任务间的协作成为可能。当然,这种共享也并不是没有风险的,如果多个线程访问同一数据空间,由于访问顺序不同,可能导致结果不一致,这种情况通常称为竞态条件(race condition),不过大多数线程库都有同步原语,以允许线程管理器的控制执行和访问;另一个要注意的问题是,线程无法给予公平执行时间,CPU 时间分配会倾向那些阻塞更少的函数。
进程的特点:独立(内存独立,cpu使用独立)启动进程开销大(速率低),进程之间很难共享数据,和数据通信,数据安全高。
线程的特点:依赖进程(内存共享,CPU使用独立)启动开销小,线程之间共享数据容易,方便通信,线程不安全。
全局解释器锁(GIL)
Python 代码执行由 Python 虚拟机 (又名解释器主循环) 进行控制。Python 在设计时是这样考虑的,在主循环中同时只能有一个控制线程在执行。对 Python 虚拟机的访问由 全局解释器(GIL) 控制,这个锁用于,当有多个线程时保证同一时刻只能有一个线程在运行。
由于 Python 的 GIL 的限制,多线程更适合 I/O 密集型应用( I/O 释放了 GIL,可以允许更多的并发),对于计算密集型应用,为了实现更好的并行性,适合使用多进程,已便利用 CPU 的多核优势。Python 的多进程相关模块:subprocess、multiprocessing、concurrent.futures
在一个进程的内部,要同时干多件事,就需要同时运行多个‘子任务’,把进程内的这些‘子任务’叫做线程。
线程通常叫做轻型的进程,是共享内存空间的并发执行的多任务,每一个线程都共享一个进程的资源。
线程是最小的执行单元,而进程由至少一个线程组成,如何调度进程和线程,完全由操作系统决定,程序自己不能决定什么时候执行,执行多长时间。
模块:
1._thread模块 低级模块
2.threading模块 高级模块 对_thread进行了封装
threading 模块除了包含 _thread 模块中的所有方法外,还提供的其他方法:
threading.currentThread(): 返回当前的线程变量。
threading.enumerate(): 返回一个包含正在运行的线程的list。正在运行指线程启动后、结束前,不包括启动前和终止后的线程。
threading.activeCount(): 返回正在运行的线程数量,与len(threading.enumerate())有相同的结果。
除了使用方法外,线程模块同样提供了Thread类来处理线程,Thread类提供了以下方法:run(): 只能执行主线程,即 只能执行一个线程
start():多个线程 并行执行
join([time]):强制调用某个线程进入执行状态,本线程礼让CPU资源,进入阻塞,休眠状态,直至某个线程退出/抛异常/超时 本线程才进入就绪状态 等待至线程中止。这阻塞调用线程直至线程的join() 方法被调用中止-正常退出或者抛出未处理的异常-或者是可选的超时发生。#保证主线程不死
isAlive(): 返回线程是否活动的。
getName(): 返回线程名。
setName(): 设置线程名。
启动线程有两种方式
一、函数方式
#usr/bin/python
#-*-coding:utf-8-*-
import threading,time
def run(num):
print('子线程(%s)开始'%(threading.current_thread().name))
#实现线程的功能
# time.sleep(2)
print('打印',num)
time.sleep(2)
print('子线程(%s)结束' % (threading.current_thread().name))
if __name__=='__main__':
#任何进程默认就会启动一个线程,成为主线程,主线程可以启动新的子线程
#current_thread():返回当前线程的实例
print('主线程(%s)开启'%(threading.current_thread().name))
#创建子线程
#target 指定线程要执行的代码
# name: 线程的名称
# args 子线程传递值
t=threading.Thread(target=run,name='runThread',args=(1,))
t.start()
#等待线程结束
t.join()
print('主线程(%s)结束' % (threading.current_thread().name))
/Users/liuqian/venv/learn/bin/python /Users/liuqian/PycharmProjects/learn/线程/启动一个线程.py
主线程(MainThread)开启
子线程(runThread)开始
打印 1
子线程(runThread)结束
主线程(MainThread)结束
Process finished with exit code 0
线程间共享数据
多线程和多进程最大的不同在于,多进程中同一个变量各自有一份拷贝存在每个进程中,互不影响。而多线程中所有变量都由所有线程共享,所以任何一个变量都可以被任意一个线程修改,因此线程之间共享数据最大的危险在于多个线程同时修改一个变量,容易把内容改乱了。
#usr/bin/python
#-*-coding:utf-8-*-
import threading,time
num=0
def run(n):
global num
for i in range(100):
num=num+n
num=num-n
if __name__=='__main__':
t1=threading.Thread(target=run,args=(6,))
t2=threading.Thread(target=run, args=(9,))
t1.start()
t2.start()
t1.join()
t2.join()
print('num',num)
/Users/liuqian/venv/learn/bin/python /Users/liuqian/PycharmProjects/learn/线程/启动一个线程.py
num 0
Process finished with exit code 0
若是改为range(10000000)
#usr/bin/python
#-*-coding:utf-8-*-
import threading,time
num=0
def run(n):
global num
for i in range(10000000):
num=num+n
num=num-n
if __name__=='__main__':
t1=threading.Thread(target=run,args=(6,))
t2=threading.Thread(target=run, args=(9,))
t1.start()
t2.start()
t1.join()
t2.join()
print('num',num)
/Users/liuqian/venv/learn/bin/python /Users/liuqian/PycharmProjects/learn/线程/启动一个线程.py
num -21
Process finished with exit code 0
import threading
import time
# 资源共享
nums=10
def something(x):
for i in range(1,x):
global nums
nums=nums+1
print(nums)
time.sleep(0.2) #停留时间 /秒
threading._start_new_thread(something,(11,))
threading._start_new_thread(something,(11,))
input()
/Users/liuqian/venv/learn/bin/python /Users/liuqian/PycharmProjects/learn/线程/启动一个线程.py
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
上面例子可以看出执行次数多了之后数据就会乱,出错
解决上述问题就需要用到线程锁,线程锁解决数据混乱问题
#锁对象lock = threading.Lock() lock.acquire()上锁 lock.release()解锁
#确保了这段代码只能由一个线程从头到尾的完整执行
#阻止了多线程的并发执行,包含锁的某段代码实际上只能以单线程执行,所以效率大大的降低了
#由于可以存在多个锁,不同线程持有不同的锁,并试图获取其他的锁,可能造成死锁,导致多个线程挂起,这样只能靠操作系统强制终止 with lock可以自动上锁与解锁
import threading,time
#锁对象
lock = threading.Lock()
num=0
def run(n):
global num
for i in range(10000000):
# 锁
#确保了这段代码只能由一个线程从头到尾的完整执行
#阻止了多线程的并发执行,包含锁的某段代码实际上只能以单线程执行,所以效率大大的降低了
#由于可以存在多个锁,不同线程持有不同的锁,并试图获取其他的锁,可能造成死锁,导致多个线程挂起,这样只能靠操作系统强制终止
"""
lock.acquire()
try:
num=num+n
num=num-n
finally:
#修改完一定要释放锁
lock.release()
"""
#与上面代码功能相同,with lock可以自动上锁与解锁
with lock:
num=num+n
num=num-n
if __name__=='__main__':
t1=threading.Thread(target=run,args=(6,))
t2=threading.Thread(target=run, args=(9,))
t1.start()
t2.start()
t1.join()
t2.join()
print('num',num)
二、类方式
import threading,time
class Mythread(threading.Thread):
def __init__(self):
threading.Thread.__init__(self)
print("Mythread __init__")
def run(self):
for i in range(10):
print(self.getName(),i)
time.sleep(0.1)#停留的时间(单位为秒)
#创建两个线程让他们同时执行
t1 = Mythread()
t2 = Mythread()
t1.start()
t2.start()
input()#保证主线程不死
/Users/liuqian/venv/learn/bin/python /Users/liuqian/PycharmProjects/learn/线程/启动一个线程.py
Mythread __init__
Mythread __init__
Thread-1 0
Thread-2 0
Thread-1 1
Thread-2 1
Thread-1 2
Thread-2 2
Thread-1 3
Thread-2 3
Thread-1 4
Thread-2 4
Thread-1 5
Thread-2 5
Thread-2 6
Thread-1 6
Thread-2 7
Thread-1 7
Thread-2 8
Thread-1 8
Thread-2 9
Thread-1 9
import threading,time
class Mythread(threading.Thread):
def __init__(self):
threading.Thread.__init__(self)
print("Mythread __init__")
def run(self):
for i in range(10):
print(self.getName(),i)
time.sleep(0.1)#停留的时间(单位为秒)
#创建两个线程让他们同时执行
t1 = Mythread()
t2 = Mythread()
t1.start()
t2.start()
input()#保证主线程不死
for i in range(10):
print(threading.current_thread().getName(),i)
time.sleep(0.1)
if i==5:
#谁那个人线程调用 哪个线程停止
#t1:强制停留0.2 秒时长 就开始恢复到就绪状态
t1.join(0.2)
new_th=threading.enumerate()
print('正在运行的线程:',new_th)
/Users/liuqian/venv/learn/bin/python /Users/liuqian/PycharmProjects/learn/线程/启动一个线程.py
Mythread __init__
Mythread __init__
Thread-1 0
Thread-2 0
Thread-1 1
Thread-2 1
Thread-1 2
Thread-2 2
Thread-1 3
Thread-2 3
Thread-1 4
Thread-2 4
Thread-2 5
Thread-1 5
Thread-2 6
Thread-1 6
Thread-2 7
Thread-1 7
Thread-2 8
Thread-1 8
Thread-2 9
Thread-1 9
案例:抢票import threading,time
nums=50
count=0
def something(x):
global nums,count
while True:
if nums==0:
return
nums=nums-1
count=count+1
print('{0}抢到了{1} 剩余{2}'.format(x,count,nums))
threading._start_new_thread(something,('妞妞',))
threading._start_new_thread(something,('小红',))
threading._start_new_thread(something,('小明',))
print(threading.enumerate())
input()
/Users/liuqian/venv/learn/bin/python /Users/liuqian/PycharmProjects/learn/线程/抢票.py
[<_MainThread(MainThread, started 140735708758912)>]
妞妞抢到了1 剩余49
妞妞抢到了2 剩余48
小红抢到了3 剩余47
小红抢到了4 剩余46
小红抢到了5 剩余45
妞妞抢到了6 剩余44
妞妞抢到了7 剩余43
妞妞抢到了8 剩余42
妞妞抢到了9 剩余41
妞妞抢到了10 剩余40
小明抢到了11 剩余39
小明抢到了12 剩余38
小明抢到了13 剩余37
小明抢到了14 剩余36
妞妞抢到了15 剩余35
妞妞抢到了16 剩余34
妞妞抢到了17 剩余33
妞妞抢到了18 剩余32
妞妞抢到了19 剩余31
妞妞抢到了20 剩余30
小明抢到了21 剩余29
小明抢到了22 剩余28
妞妞抢到了23 剩余27
妞妞抢到了24 剩余26
小明抢到了25 剩余25
小明抢到了26 剩余24
妞妞抢到了27 剩余23
妞妞抢到了28 剩余22
妞妞抢到了29 剩余21
小明抢到了30 剩余20
小明抢到了31 剩余19
妞妞抢到了32 剩余18
妞妞抢到了33 剩余17
小红抢到了34 剩余16
小红抢到了35 剩余15
妞妞抢到了36 剩余14
妞妞抢到了37 剩余13
妞妞抢到了38 剩余12
小红抢到了39 剩余11
小红抢到了40 剩余10
妞妞抢到了41 剩余9
妞妞抢到了42 剩余8
妞妞抢到了43 剩余7
小红抢到了44 剩余6
小红抢到了45 剩余5
妞妞抢到了46 剩余4
妞妞抢到了47 剩余3
妞妞抢到了48 剩余2
妞妞抢到了49 剩余1
妞妞抢到了50 剩余0
更完美的:import threading,time
nums = 50
count = 0
lock=threading.Lock() #声明一把锁
Cond=threading.Condition(lock=lock)#管理一把锁
class qiangpiao(threading.Thread):
def __init__(self,name):
threading.Thread.__init__(self)
self.setName(name)
def run(self):
global nums,count
while True:
Cond.acquire()#当哪个线程获得了资源 就加上锁
if nums == 0:
return
nums = nums - 1
count = count + 1
print('{0}抢到了{1} 剩余{2}'.format(self.getName(),count,nums))
time.sleep(0.2)
Cond.release()#当哪个线程完成了处理 就解开锁
if __name__ =='__main__':
huangniu=qiangpiao('黄牛')
shouji=qiangpiao('手机')
chuangkou=qiangpiao('窗口')
huangniu.start()
shouji.start()
chuangkou.start()
/Users/liuqian/venv/learn/bin/python /Users/liuqian/PycharmProjects/learn/线程/抢票.py
黄牛抢到了1 剩余49
黄牛抢到了2 剩余48
黄牛抢到了3 剩余47
黄牛抢到了4 剩余46
黄牛抢到了5 剩余45
黄牛抢到了6 剩余44
黄牛抢到了7 剩余43
黄牛抢到了8 剩余42
窗口抢到了9 剩余41
窗口抢到了10 剩余40
窗口抢到了11 剩余39
窗口抢到了12 剩余38
窗口抢到了13 剩余37
窗口抢到了14 剩余36
黄牛抢到了15 剩余35
黄牛抢到了16 剩余34
黄牛抢到了17 剩余33
黄牛抢到了18 剩余32
窗口抢到了19 剩余31
窗口抢到了20 剩余30
黄牛抢到了21 剩余29
黄牛抢到了22 剩余28
窗口抢到了23 剩余27
窗口抢到了24 剩余26
黄牛抢到了25 剩余25
黄牛抢到了26 剩余24
黄牛抢到了27 剩余23
黄牛抢到了28 剩余22
黄牛抢到了29 剩余21
黄牛抢到了30 剩余20
黄牛抢到了31 剩余19
手机抢到了32 剩余18
手机抢到了33 剩余17
手机抢到了34 剩余16
手机抢到了35 剩余15
手机抢到了36 剩余14
手机抢到了37 剩余13
手机抢到了38 剩余12
手机抢到了39 剩余11
手机抢到了40 剩余10
窗口抢到了41 剩余9
窗口抢到了42 剩余8
窗口抢到了43 剩余7
窗口抢到了44 剩余6
窗口抢到了45 剩余5
黄牛抢到了46 剩余4
黄牛抢到了47 剩余3
窗口抢到了48 剩余2
窗口抢到了49 剩余1
窗口抢到了50 剩余0
超级案例:import threading,time
zhenglong=[] #蒸笼
#创建2把锁 一把蒸馒头的锁 由伙夫掌管
# 另一把吃馒头的锁 由吃货 掌管
zheng_lock=threading.Lock()
zheng_Cond=threading.Condition(lock=zheng_lock)
chi_lock=threading.Lock()
chi_Cond=threading.Condition(lock=chi_lock)
class huofu(threading.Thread):
def __init__(self,name):
threading.Thread.__init__(self)
self.setName(name)
def run(self):
while True:
chi_Cond.acquire()#被谁唤醒
if len(zhenglong)==0:
#开始蒸馒头
for i in range(1,11):
zhenglong.append(i)
time.sleep(0.1)
print('正在蒸第{0}个馒头'.format(i))
print('馒头蒸完了 唤醒吃货们开始吃馒头..')
chi_Cond.notify_all()#唤醒了吃货们..
chi_Cond.release()#唤醒了伙夫 他就释放
#伙夫 进入休眠
zheng_Cond.acquire()
zheng_Cond.wait()
zheng_Cond.release()
class chihuo(threading.Thread):
def __init__(self,name):
threading.Thread.__init__(self)
self.setName(name)
def run(self):
while True:
chi_Cond.acquire()#同一时刻只有 一个吃货在获取 吃馒头的资源
global zhenglong
if len(zhenglong)==0:
# 开始呼唤伙夫(只叫一次) 蒸馒头 我和其他吃货一起进入休眠
zheng_Cond.acquire()#锁定叫醒伙夫的线程
zheng_Cond.notify()#唤醒
zheng_Cond.release()#释放
chi_Cond.wait()
else:
mantou=zhenglong.pop()
print('{0} 吃了第{1}个馒头 剩余{2}个馒头'.format(self.getName(),mantou,len(zhenglong)))
time.sleep(0.1)
chi_Cond.release()
W=huofu('伙夫')
youran=chihuo('悠然')
niuniu=chihuo('妞妞')
xiaoming=chihuo('小明')
W.start()
youran.start()
niuniu.start()
xiaoming.start()
#保证主线程不死
input()
其结果是无限循环