代码示例
join
//
import threading
import time
def thread_job():
print("t1 start \n")
for i in range(10):
time.sleep(0.2)
print("t1 finish\n")
def t2_job():
print("t2 start\n")
print("t2 finish\n")
def t():
add_thread = threading.Thread(target=thread_job)
add_thread.start()
add_thread.join()
t2 = threading.Thread(target=t2_job)
t2.start()
t2.join()
print("all finish\n")
if __name__ == '__main__':
t()
结果
t1 start
t1 finish
t2 start
t2 finish
all finish
queue
import threading
from queue import Queue
def job(l,q):
for i in range(len(l)):
l[i] = l[i]**2
q.put(l)
def multi():
q = Queue()
threads= []
data = [[1,2,3],[3,4,5],[4,4,4],[5,5,5]]
for i in range(4):
t = threading.Thread(target=job,args=(data[i],q))
t.start()
t.join()
threads.append(t)
results = []
for _ in range(4):
results.append(q.get())
print(results)
if __name__ == '__main__':
multi()
结果
[[1, 4, 9], [9, 16, 25], [16, 16, 16], [25, 25, 25]]
利用队列Queue实现一个多并发“线程池”效果的Socket程序
GIL的理解(全局解释器锁)
GIL无疑就是一把全局排他锁。毫无疑问全局锁的存在会对多线程的效率有不小影响。甚至就几乎等于Python是个单线程的程序。
因为GIL的存在,只有IO Bound场景下得多线程会得到较好的性能
如果对并行计算性能较高的程序可以考虑把核心部分也成C模块,或者索性用其他语言实现
GIL在较长一段时间内将会继续存在,但是会不断对其进行改进
什么是全局解释器锁
每个CPU在同一时间只能执行一个线程,那么其他的线程就必须等待该线程的全局解释器,使用权消失后才能使用全局解释器,即使多个线程直接不会相互影响在同一个进程下也只有一个线程使用cpu,这样的机制称为全局解释器锁(GIL)。GIL的设计简化了CPython的实现,使的对象模型包括关键的内建类型,如:字典等,都是隐含的,可以并发访问的,锁住全局解释器使得比较容易的实现对多线程的支持,但也损失了多处理器主机的并行计算能力。
全局解释器锁的好处
1)、避免了大量的加锁解锁的好处
2)、使数据更加安全,解决多线程间的数据完整性和状态同步
全局解释器的缺点
多核处理器退化成单核处理器,只能并发不能并行。
GIL的作用:
多线程情况下必须存在资源的竞争,GIL是为了保证在解释器级别的线程唯一使用共享资源(cpu)。
摘自牛客原文链接
假设我们有四个任务需要处理,处理方式肯定是要玩出并发的效果,解决方案可以是:
方案一:开启四个进程
方案二:一个进程下,开启四个线程
单核情况下,分析结果:
如果四个任务是计算密集型,没有多核来并行计算,方案一徒增了创建进程的开销,方案二胜
如果四个任务是I/O密集型,方案一创建进程的开销大,且进程的切换速度远不如线程,方案二胜
现在大部分的软件都是IO密集型,所以开多线程
多核情况下,分析结果:
如果四个任务是计算密集型,多核意味着并行计算,在python中一个进程中同一时刻只有一个线程执行用不上多核,方案一胜
如果四个任务是I/O密集型,再多的核也解决不了I/O问题,方案二胜
结论:
现在的计算机基本上都是多核,python对于计算密集型的任务开多线程的效率并不能带来多大性能上的提升,甚至不如串行(没有大量切换),但是,对于IO密集型的任务效率还是有显著提升的。
原文链接