gil的存在一直备受争议,这使得python程序无法真正利用现代操作系统的多进程特性。需要注意的是,i/o图形处理和numpy数学计算等耗时的操作都发生在gil之外,基本上不受影响。真正受影响的是python字节码的执行,gil会导致性能瓶颈。简而言之,只有当纯python用于cpu密集型多线程时,gil才会成为问题。
什么是gil?pythons代码执行由python虚拟机控制(也叫解释器主循环,cpython版本)。python最初被设计成只有一个线程在解释器主循环中运行。也就是说,每个cpu在任何时候都只有一个线程在解释器中运行。对python虚拟机的访问由全局解释锁gil控制,它控制一次只能运行一个线程。-单核cpu下的多线程其实是并发的,不是并行的。
并发和并行的区别
并发性:两个或多个事件在同一时间间隔内发生,或者交替做不同的事件,或者交替执行不同的代码块的能力。并行性:两个或多个事件同时发生,或者同时执行不同事件,或者同时执行不同代码块的能力。
并发和并行的含义
并发和并行都可以处理"多任务",两者的主要区别在于多任务是否"同时进行"。但是涉及到任务分解(有顺序依赖耦合度高的任务不能并行)、任务操作(互斥、加锁、共享等。),以及结果合并。
python中的多线程在python多线程下,每个线程的执行模式如下:
有两种机制可以获得gil并切换到这个线程来执行正在运行的代码:指定数量的字节码指令(100)和15毫秒的固定时间。线程主动放弃控制,并将线程设置为睡眠状态以释放gil。再次重复上述步骤。在python2中,当解释器解释任何python代码的执行时,都需要先获得这个锁(只有一个获得了gil的线程在同时运行,其他所有线程都在等待gil发布)。如果是没有i/o操作的纯计算程序,解释器会每100次操作释放一次锁,让其他线程有机会执行(这个数字可以通过调整)。正是这样的设定,多线程cpu密集型计算显得很鸡肋,下面就说说为什么。
在python3中,gil不使用滴答来计数(100次,释放gil),而是使用定时器来代替(执行时间达到15ms阈值后,当前线程释放。放gil),使得计算次数更多,释放次数更少,对cpu密集型程序更友好,但还是没有解决gil一次只能执行一个线程的问题,所以效率还是不尽如人意。
pythons多线程一个鸡肋?cpu密集型(各种循环处理,计数等。),在这种情况下,滴答数很快就会达到阈值,然后触发gil的释放和重新竞争(多线程来回切换需要资源),所以python中的多线程对cpu密集型代码并不友好,会触发相当频繁的线程切换。
io密集型(文件处理、网络爬虫等。),多线程可以有效提高效率(如果单线程下有io操作,就会等待io,造成不必要的时间浪费,而开启多线程可以在线程a等待的同时自动切换到线程b,不会浪费cpu资源,从而提高程序执行效率。一个线程从gil获得一个消息,然后等待返回消息(阻塞),python在这个时候释放gil。其他线程得到gil发送的消息,然后等待返回消息(阻塞)........................................................................................................................................................所以python的多线程对io密集型代码很友好。
结论是什么?i/o密集型使用多线程并发执行提高效率,计算密集型使用多处理并行执行提高效率。通常程序中既包含io操作,又包含计算操作,所以这种情况下,在开始并发任务之前,可以先测试一下,测试一下多线程多进程哪种方法效率高。
请注意:多核多线程比单核多线程差。多核多进程下,cpu1释放gil后,其他cpu上的线程会竞争,但gil可能马上被cpu1拿走。cpu2释放gil后,其他cpu上被唤醒的线程会被唤醒,等待切换时间后再进入待调度状态,这样会导致线程抖动,效率降低。
多线程下的cpu密集型计算并非不可救药。ctypes可以绕过gil,让py直接调用c动态库的任何导出函数。我们要做的就是用c/c把关键部分写成python扩展,而且ctypes会在调用c函数之前释放gil。
同时可以了解下一个进程,也就是微线程。
协成最大的优势就是极高的执行效率。因为子程序切换不是线程切换,而是由程序本身控制,所以没有线程切换的开销。与多线程相比,线程越多,协程的性能优势就越明显。
第二个优点是不需要多线程锁定机制,因为只有一个线程,不存在同时写变量的,共享资源在进程中不加锁控制,只是判断状态,所以执行效率比多线程高很多。
因为进程是一个线程执行的,如何使用多核cpu?最简单的方法就是多进程协调,既充分利用了多核,又充分发挥了协调的高效率,可以获得极高的性能。
传递对象参数(方法1)
通过查看远程方法,发现fun接口的参数类型是paramtype,它有两个属性,p1和p2。
客户端客户端(url)
(参数类型)
m.p1