进程与线程
进程是资源分配的最小单位,他是操作系统进行资源分配和调度运行的基本单位,一旦创建一个进程就会分配一定的资源,通俗理解一个正在运行的程序就是一个进程,例如微信,QQ都是进程。
线程是程序运行的最小单位,实际上进程只负责分配资源,而利用这些资源执行程序的是线程,一个进程中至少有一个线程来执行程序。同一进程中的线程共享进程的全部资源。
1.关系对比:线程是依附在进程里面的,没有进程就没有线程,一个进程默认提供一条线程,进程可以创建多个线程。
2.区别对比:创建进程的资源开销比较大,进程是操作系统资源分配的基本单位,线程是CPU调度的基本单位。
3.优缺点:进程可以使用多核,但资源开销大。线程资源开销小,不能使用多核。
多线程——Threading
线程是程序执行的最小单位。
1.Python中的GIL
又叫全局解释器锁,每个线程在执行的过程中都需要先获取GIL,保证同一时刻只有一个线程在运行,目的是解决多线程同时竞争程序中的全局变量而出现的线程安全问题。也就是说多线程下每个线程在执行的过程中都需要先获取GIL,保证同一时刻只有一个线程在运行,即在多核CPU中,多线程同一时刻也只有一个线程在运行。看似是多线程实际上是单线程。它并不是python语言的特性,仅仅是由于历史的原因在CPython解释器中难以移除,因为python语言运行环境大部分默认在CPython解释器中。
2.计算密集型和IO密集型
计算密集型:要进行大量的数值计算,例如进行上亿的数字计算、计算圆周率、对视频进行高清解码等等。这种计算密集型任务虽然也可以用多任务完成,但是花费的主要时间在任务切换的时间,此时CPU执行任务的效率比较低。
IO密集型:涉及到网络请求(time.sleep())、磁盘IO的任务都是IO密集型任务,这类任务的特点是CPU消耗很少,任务的大部分时间都在等待IO操作完成(因为IO的速度远远低于CPU和内存的速度)。对于IO密集型任务,任务越多,CPU效率越高,但也有一个限度。
3.总结
1.Python语言和GIL没有什么关系。仅仅是由于历史原因在Cpython虚拟机(解释器),难以移除GIL。
2.GIL:全局解释器锁。每个线程在执行的过程都需要先获取GIL,保证同一时刻只有一个线程可以执行代码。
3.线程释放GIL锁的情况: 在IO操作等可能会引起阻塞的system call之前,可以暂时释放GIL,但在执行完毕后,必须重新获取GIL Python 3.x使用计时器(执行时间达到阈值后,当前线程释放GIL)或Python 2.x,tickets计数达到100。
4.Python使用多进程是可以利用多核的CPU资源的。
5.多线程爬取比单线程性能有提升,因为遇到IO阻塞会自动释放GIL锁。
6. GIL本质是一把互斥锁,将线程从并发变为串行,牺牲效率保证数据安全。
7. 不同的数据要加不同的互斥锁,GIL保护的是解释器级别的安全。
8.应用场合
对于计算密集型,多进程优于多线程。对于IO密集型,多线程优于多进程。
4.解决GIL问题的方案:
1.使用其它语言,例如C,Java
2.使用其它解释器,如java的解释器jython
3.使用多进程
2.多线程的基本语法
查看线程名字,数量
import threading
def thread_job():
print("当前线程名字:",threading.current_thread())
def main():
add_thread=threading.Thread(target=thread_job())
print(threading.active_count())#显示的线程数量
print(threading.enumerate())#枚举出来的线程对象列表
print(threading.current_thread())#当前的线程对象
print(threading.current_thread().name) # 当前的线程对象名字
if __name__ == '__main__':
main()
2.带参数,并且有返回值
import threading
import time
def job1(data,list1):
for i in data:
list1.append(i**2)
return list1
def main():
List=[]
thread1 = threading.Thread(target=job1,args=(range(5),List) ,name="job1")
thread2 = threading.Thread(target=job1, args=(range(10,15), List), name="job1")
thread1.start()
thread2.start()
thread1.join()#等线程运行完之后在执行主线程的语句
thread2.join()
return List
if __name__ == '__main__':
time_start=time.time()
print(main())
time_end = time.time()
print('totally cost', time_end - time_start)
3.无参数,无返回值,守护主线程
import threading
import time
def job1():
print("job1,开始")
time.sleep(3)
print("job1,完了")
def job2():
print("job2,开始")
for i in range(10):
print(i)
time.sleep(0.2)
print("job2,完了")
def main():#子线程
thread1 = threading.Thread(target=job1,name="job1")
thread2 = threading.Thread(target=job2, name="job2")
# 守护主线程:主线程结束后,子线程无论是否执行完了都要结束。
#方法一
# thread1 = threading.Thread(target=job1, name="job1", daemon=True)
# 方法二
# thread1.setDaemon(True)
# thread2.setDaemon(True)
thread1.start()
thread2.start()
thread1.join()#等线程运行完之后在执行主线程的语句
thread2.join()
if __name__ == '__main__':#主线程
main()
print("任务全部结束")
4.线程池(开启线程池后,每次开启4个线程,也就是一次只执行4个请求)
import time
import threadpool
def job(url):
time.sleep(3)
print(str(url)+"\n")
if __name__ == '__main__':
urls=[i for i in range(7)]
pool=threadpool.ThreadPool(4)#建立线程池,开启4个线程
request=threadpool.makeRequests(job,urls)
for re in request:
pool.putRequest(re)
pool.wait()
5.简单的线程
import threading
import time
def job1():
print(threading.current_thread()) # 当前的线程对象名字
print("job1,开始")
time.sleep(3)
print("job1,完了")
if __name__ == '__main__':#主线程
time1=time.time()
t=[]
for i in range(5):
thread1 = threading.Thread(target=job1, name="job1")
t.append(thread1)
thread1.start()
for t1 in t:
t1.join()
print("运行时间:",time.time()-time1)
6.继承方法写入
import time
import threading
class MyThread(threading.Thread):
def __init__(self,A,value):
threading.Thread.__init__(self)
self.value=value
self.A = A
def run(self):
self.A.append(self.value)
print((self.name,self.A))
if __name__ == '__main__':
A=[]
t1=time.time()
lis=[]
for i in range(5):
t=MyThread(A,i)
lis.append(t)
t.start()
for i in lis:
i.join()
print("******************")
print(time.time()-t1)
print(A)
7.当多个线程共享一个参数变量的时候,需要对其数值进行修改需要,添加锁,在某个线程访问的时候,其他线程不能访问。
死锁
1.程序内存在问题,导致锁不能及时被释放(增加try except finally方法)
2.在线程共享多个资源的时候,如果两个线程分别占有一部分资源的时候,并且同时想要获取对方的资源,就会造成死锁。
避免死锁的方法
1.银行家算法
2.添加等待时间
多进程
import multiprocessing
import threading
import time
import os
def sing(name):
print("sing进程编号",os.getpid())#子进程编号
print("sing父进程编号", os.getppid()) # 父进程编号
for i in range(3):
print(name+"sing..")
time.sleep(1)
def dance(name):
print("dance进程编号",os.getpid())#子进程编号
print("dance父进程编号", os.getppid()) # 父进程编号
for i in range(3):
print(name+"dance..")
time.sleep(1)
def job(name):
for i in range(3):
time.sleep(0.2)
with open('ts.txt', 'a', encoding='utf-8') as f:
text = str(i)+name+"sing\n"
f.write(text)
if __name__ == '__main__':
print("主进程编号", os.getpid()) # 进程编号
sing_process=multiprocessing.Process(target=sing,args=("晓明",))#元组传参,守护主进程,方法1
dance_process=multiprocessing.Process(target=dance,args=("晓明",))#元组传参
# dance_process.daemon=True#守护主进程(主进程执行完了,子进程也停止)方法2
sing_process.start()
dance_process.start()
print("主进程执行完了")
#创建多个进程
t1=time.time()
list1 = ["1", "2", "3"]
ls=[]
for str1 in list1:
process = multiprocessing.Process(target=job, args=(str1))
# process = threading.Thread(target=job, args=(str1))
ls.append(process)
process.start()
for jjj in ls:
jjj.join()
print("时间:",time.time()-t1)