进程线程与GIL全局解释器锁详解

Posted zzzynx

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了进程线程与GIL全局解释器锁详解相关的知识,希望对你有一定的参考价值。

技术图片进程与线程的关系:

1.   线程是最小的调度单位
2.  进程是最小的管理单元
3.  一个进程必须至少一个线程
4.  没有线程,进程也就不复存在

线程特点:

3     线程的并发是利用cpu上下文的切换(是并发,不是并行)
4     多线程执行的顺序是无序的
5     多线程共享全局变量
6     线程是继承在进程里的,没有进程就没有线程
7     GIL全局解释器锁
8     只要在进行耗时的IO操作的时候,能释放GIL,所以只要在IO密集型的代码里,用多线程就    
9        很合适

线程详解:

import threading  # ---》导入模块
    # def func(n):
    #     print(task,n)
    # t = threading.Thread(target = func,args = (1,))
    # t.start()
    #
    #
    # for i in range(10):        ---》使t的内容循坏输出10行
    #     t = threading.Thread(target = func,args = (t-%s%i,))  ---》target=函数名 args要求以元组形式传参,当参数只有一个时,以(参数,)的格式传参。
    #     t.start()       ----》固定启动线程

    # 多线程共享全局变量

    # g=0    ---》设置一个全局变量
    # def test():
    #     global g              ----》线程共享全局变量时需要建立在声明global+全局变量上
    #     for i in range(10):
    #         g +=1
    #     print(g)
    # def test1():
    #     global g
    #     for i in range(10):
    #         g +=1
    #     print(g)
    # t1=threading.Thread(target=test)     ---->用线程调用test函数的结果,没有参数时,只需要输入target就行
    # t2=threading.Thread(target=test1)
    # t1.start()
    # t2.start()

 GIL全局解释器锁:

技术图片

#GIL全局解释器锁        ---作用是保证最多只有一个线程在使用全局变量g_num,但是不保证赋值成功
lock = threading.Lock()   ----添加互斥锁,作用是使两个线程不再并发处理,使赋值成功,注意L要大写
g_num = 0
def test1():
    global g_num
    lock.acquire()        ---》在循坏前锁上
    for i in range(100000):
        g_num +=1
    lock.release()      ----》赋值结束后解放锁
def test2():
    global g_num
    lock.acquire()
    for i in range(100000):
        g_num +=1
    lock.release()
t1=threading.Thread(target=test1)       ---》注意T要大写
t2=threading.Thread(target=test2)
t1.start()      #  导致结果不对的原因(1):主线程处理py文件速度比另外两个线程赋值速度快,在没赋值结束时就又开始新一轮的赋值
t2.start()

# 解决方法(1):
# 在后面加上两行
t1.join()   #----》即主线程等待每轮赋值结束后才重启下一轮赋值,但是这样也还是不对,只能让成功次数上升
t2.join()   # 因为赋值成功率不是100%,次数越多越有可能赋值失败。
print(g_num)

解决方法(2):
添加互斥锁:lock = threading.Lock() ,保证赋值成功,缺点是速度慢

进程详解:

# 一个程序运行起来之后,代码+用到的资源称之为进程,它是操作系统分配资源的基本单位,不仅可以通过线程完成多任务,进程也是可以的
# 进程之间是相互独立的,结果互不影响也无法共享全局变量,等待上一个进程结束,下一个进程再执行。
# cpu密集的时候适合用多进程
#cpu效率高,耗资源多,

import multiprocessing    --》导入进程模块
import time  ----》导入快捷键,将鼠标停在关键词上alt 加 enter 选择导入模块


def test1(n):
    time.sleep(2)       ---》两秒后打印执行任务完毕,再到第二个进程
    print(task,n)

def test2(n):
    time.sleep(2)
    print(task,n)
if __name__ == __main__:    --->相当于调用test1()和test(2),这是主进程的作用
    p1 = multiprocessing.Process(target=test1,args=(1,))     --》注意P的大写
    p2 = multiprocessing.Process(target=test2,args=(1,))
    p1.start()      ---》此进程负责调用p1进程
    p2.start()     ---》负责调用p2进程

进程池:

import multiprocessing
import time
g_num = 0
def test1(n):
    for i in range(n):
        time.sleep(1)
        print(test1, i)

def test2(n):
    for i in range(n):
        time.sleep(1)
        print(test2, i)
def test3(n):
    for i in range(n):
        time.sleep(1)
        print(test3, i)

def test4(n):
    for i in range(n):
        time.sleep(1)
        print(test4, i)

if __name__ == __main__:                  #此操作必须存在,不然无法调用
    pool = multiprocessing.Pool(3)          #把进程声明出来括号里不写东西说明无限制,如果写数字,就是最大的进程数,即允许几个进程并发。池外的串行
    pool.apply_async(test1,(5,))           #用pool去调用函数test1,参数为5格式为(5,)
    pool.apply_async(test2,(5,))
    pool.apply_async(test3,(5,))
    pool.apply_async(test4,(5,))          #以上三个进程并发处理,同时输出赋值,而4进程串行输出,因为在进程池外**
    pool.close()                            #close必须在join的前面
  

 

以上是关于进程线程与GIL全局解释器锁详解的主要内容,如果未能解决你的问题,请参考以下文章

并发编程——GIL全局解释器锁死锁现象与递归锁信号量Event事件线程queue

Python入门学习-DAY36-GIL全局解释器锁死锁现象与递归锁信号量Event事件线程queue

Python进程与线程及GIL(全局解释器锁)

GIL(全局解释器锁)与互斥锁

线程与全局解释器锁(GIL)

GIL全局解释器锁和进程池.线程池