福田网站-建设深圳信科,免费网站建设塔山双喜,免费自己做网站,嘉兴seo外包公司费用1 什么是gil锁#xff0c;有什么作用#xff1f; 2 python的垃圾回收机制是什么样的#xff1f; 3 解释为什么计算密集型用多进程#xff0c;io密集型用多线程。 1 什么是gil锁#xff0c;有什么作用#xff1f;
1 GIL#xff1a;Global Interpreter Lock又称全局解释器…1 什么是gil锁有什么作用 2 python的垃圾回收机制是什么样的 3 解释为什么计算密集型用多进程io密集型用多线程。 1 什么是gil锁有什么作用
1 GILGlobal Interpreter Lock又称全局解释器锁。本质就是一个互斥锁
2 保证了cpython进程中得每个线程必须获得这把锁才能执行不获得不能执行
3 使得在同一进程内任何时刻仅有一个线程在执行
4 gil锁只针对于cpython解释器----》JPythonPyPyCPython***作用***1 保护Python对象免受多线程并发访问的破坏。2 确保在多线程环境中只有一个线程执行Python字节码。3 GIL的存在使得在CPU密集型任务中Python的多线程并不能充分发挥多核CPU的优势。因为只有一个线程能够执行字节码其他线程会被阻塞。***为什么要有gil锁***python是动态强类型语言因为有垃圾回收机制如果同一个进程下有多个线程同时在执行垃圾回收是垃圾回收线程【同一个进程下变量是共享的】该线程做垃圾回收时如果其他线程在运行就可能会出并发安全的问题【数据安全的问题】由于当时只有单核cup【即便开启多线程同一时刻也只有一个线程在运行】作者就强行做了一个GIL锁保证在一个进程内同一时刻只有一个线程执行目的是为了防止垃圾回收线程做垃圾回收时出现数据紊乱问题所以加了gil锁**垃圾回收**是垃圾回收线程它在执行的时候其他线程是不能执行的而限于当时的条件只有单核cpu所以作者直接做了个GIL锁保证一个进程内同一时刻只有一个线程在执行。python使用引用计数为主标记清楚和隔代回收为辅来进行内存管理。所有python脚本中创建的对象都会配备一个引用计数来记录有多少个指针来指向它。当对象的引用技术为0时会自动释放其所占用的内存。假设有2个python线程同时引用一个数据a100引用计数为12个线程都会去操作该数据由于多线程对同一个资源的竞争实际上引用计数为3但是由于没有GIL锁导致引用计数只增加1引用计数为2这造成的后果是当第1个线程结束时会把引用计数减少为1当第2个线程结束时会把引用计数减少为0当下一个线程再次视图访问这个数据时就无法找到有效的内存了
import threading# 共享变量
counter 0def count_up():global counterfor _ in range(1000000):counter 1def count_down():global counterfor _ in range(1000000):counter - 1# 创建两个线程分别执行计数操作
t1 threading.Thread(targetcount_up)
t2 threading.Thread(targetcount_down)# 启动线程
t1 .start()
t2.start()# 等待两个线程执行完成
t1 .join()
t2.join()print(Counter:, counter)在上述代码中我们有两个线程一个递增 counter一个递减 counter。
理论上counter 的最终值应该是 0。但是由于GIL的存在多线程并发执行时由于GIL的保护
实际上可能并不会得到正确的结果。在这个例子中counter 的最终值可能不是 0
因为两个线程在修改 counter 时可能会发生竞争条件。2 python的垃圾回收机制是什么样的
1 参考文章https://www.jianshu.com/p/52ab268901142 什么是垃圾回收编程语言在运行过程中会定义变量---申请了内存空间---》后期变量不用了---》这个内存空间应该释放掉有些编程语言这个操作需要程序员自己做(c)像javapythongo这些语言都自带垃圾回收机制可以自动回收内存空间gc机制。3 不同语言垃圾回收的方式是不一样的python是使用如下三种方式做gc以引用计数为主标记-清除和分代回收两个算法为辅(1)引用计数算法(reference counting)每个对象都有一个引用次数的计数属性如果对象被引用了那这个数就会 加1如果引用被删除引用计数就会 减1那么当该对象的引用计数为0时就说明这个对象没有被使用垃圾回收线程就会把它回收掉释放内存。-有问题循环引用问题---》回收不了(2) 标记-清除算法(Mark and Sweep)-解决引用计数无法回收循环引用的问题对象之间通过引用连在一起节点就是各个对象从一个根对象向下找对象可以到达的标记为活动对象不能到达的是非活动对象而非活动对象就是需要被清除的。(3) 分代回收算法(Generational garbage collector)-分代回收是解决垃圾回收效率问题算法原理是Python把对象的生命周期分为三代分别是第0代、第1代、第2代。每一代使用双向链表来标记这些对象。每一代链表都有总数阈值当达到阈值的时候就会出发GC回收将需要清除的清除掉不需要清除的移到下一代。以此类推第2代中的对象存活周期最长的对象。注意python垃圾回收最核心是引用计数----》标记清除解决引用计数的循环引用问题---》分代回收解决垃圾回收的效率问题。 import gc# 创建一个循环引用的对象
class CircularReference:def __init__(self):self.circular_ref None# 创建循环引用
obj1 CircularReference()
obj2 CircularReference()
obj1.circular_ref obj2
obj2.circular_ref obj1# 手动断开引用使引用计数变为零
obj1 None
obj2 None# 手动触发垃圾回收
gc.collect()# 由于循环引用垃圾回收器会将它们回收
print(gc.garbage)在上述代码中CircularReference 类创建了两个对象 obj1 和 obj2
它们相互引用形成了循环引用。当手动断开对 obj1 和 obj2 的引用后手动调用 gc.collect()
来触发垃圾回收。垃圾回收器会检测到这个循环引用并将其回收。回收后gc.garbage 列表中将包含被
回收的对象我们可以通过查看这个列表来确认回收是否成功。3 解释为什么计算密集型用多进程io密集型用多线程。
计算是消耗cpu的代码执行算术for都是计算
io不消耗cpu打开文件写入文件网络操作都是io-如果遇到io该线程会释放cpu的执行权限cpu转而去执行别的线程由于python有GIL锁开启多条线程同一时刻只能有一条线程在执行。
如果是***计算密集型***开了多线程同一时刻只有一个线程在执行多核cpu就会浪费多核优势
如果是计算密集型我们希望多个核(cpu),都干活同一个进程下绕不过gil锁。所以我们开启多进程
gil锁只能锁住某个进程中的线程开启多个进程就能利用多核优势。***io密集型***只要遇到io就会释放cpu执行权限进程内开了多个io线程线程多半都在等待
开启多进程是不能提高效率的反而开启进程很耗费资源所以使用多线程即可。计算密集型任务主要是由CPU计算驱动对CPU的利用率要求较高。
I/O密集型任务主要是由I/O操作驱动对CPU的利用率较低。
计算密集型任务使用多进程
import multiprocessingdef calculate_square(numbers):result []for number in numbers:result.append(number * number)print(Result (in process):, result)if __name__ __main__:numbers list(range(1, 6))# 使用多进程process multiprocessing.Process(targetcalculate_square, args(numbers,))process.start()process.join()print(Main process continues...)I/O密集型任务使用多线程
import threading
import timedef simulate_io_operation():print(Start I/O operation...)time.sleep(2) # 模拟I/O操作比如文件读写或网络请求print(I/O operation completed.)if __name__ __main__:# 使用多线程t1 threading.Thread(targetsimulate_io_operation)t2 threading.Thread(targetsimulate_io_operation)t1.start()t2.start()t1.join()t2.join()print(Main thread continues...)在这两个例子中计算密集型任务使用了多进程而I/O密集型任务使用了多线程。这是因为计算密集型任务中的 GIL 限制了多线程的效果而I/O密集型任务中可以充分利用多线程的并发性。