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

原文链接 https://blog.51cto.com/altboy/1923300

进程与线程

程序并不能单独运行,只有将程序装载到内存中,系统为它分配资源才能运行,
而这种一个程序在一个数据集上的一次动态执行过程就称之为进程。

程序和进程的区别就在于:程序是指令的集合,它是进程运行的静态描述文本;

进程是程序的一次执行活动,属于动态概念。进程一般由程序、数据集、进程控制块三部分组成。
有了进程为什么还要有线程呢?

因为进程还是有缺陷的:

进程只能在一个时间干一件事,如果想同时干两件事或多件事,进程就无能为力了
进程在执行的过程中如果阻塞,例如等待输入,整个进程就会挂起,即使进程中有些工作不依赖于输入的数据,也将无法执行
那么什么是线程呢?
线程是操作系统能够进行运算调度的最小单位。它被包含在进程之中,是进程中的实际运作单位。
一条线程指的是进程中一个单一顺序的控制流,一个进程中可以并发多个线程,每条线程并行执行不同的任务
进程和线程的关系:
 1. 一个线程只能属于一个进程,而一个进程可以有多个线程,但至少有一个线程。 
 2. 资源分配给进程,同一进程的所有线程共享该进程的所有资源。
 3.  CPU分给线程,即真正在CPU上运行的是线程。 
 4. 进程:资源管理单位 (容器),线程:最小执行单位
并行和并发
并行处理:是计算机系统中能同时执行两个或更多个处理的一种计算方法。并行处理可同时工作于同一程序的不同方面。
		并行处理的主要目的是节省大型和复杂问题的解决时间。

并发处理:指一个时间段中有几个程序都处于已启动运行到运行完毕之间,且这几个程序都是在同一个处理机(CPU)上运行,
		但任一个时刻点上只有一个程序在处理机(CPU)上运行
同步与异步
同步:指一个进程在执行某个请求的时候,若该请求需要一段时间才能返回信息,那么这个进程将会一直等待下去,
		直到收到返回信息才继续执行下去。

异步:指进程不需要一直等待下去,而是继续执行下面的操作,不管其他进程的状态,当有消息返回时系统会通知
	 进程进行处理,这样可以提高执行效率

关于GIL(全局解释器锁)

Python中的线程是操作系统的原生线程,Python虚拟机使用一个全局解释器锁(Global Interpreter Lock)
来互斥线程对Python虚拟机的使用。为了支持多线程机制,一个基本的要求就是需要实现不同线程对共享资源访问的互斥,
所以引入了GIL。

GIL:在一个线程拥有了解释器的访问权之后,其他的所有线程都必须等待它释放解释器的访问权,即使这些线程的下一条指令并不会互相影响。

在调用任何Python C API之前,要先获得GIL

GIL缺点:多处理器退化为单处理器;
   优点:避免大量的加锁解锁操作 

Python的多线程:由于GIL锁,导致同一时刻,同一进程只能有一个线程被执行

总结:

对于计算密集型任务(一直在使用CPU):python的多线程并没有用

对于IO密集型任务(存在大量IO操作):python的多线程是有意义的

要想使python使用多核:只能开进程, 弊端:开销大而且切换复杂

使用多核的着重点应该放在:协程+多进程

终极思路:换C模块实现多线程
threading模块
import threading
import time

def countNum(n): # 定义某个线程要运行的函数

    print("running on number:%s" %n)

    time.sleep(3)

if __name__ == '__main__':

    t1 = threading.Thread(target=countNum,args=(23,)) #生成一个线程实例
    t2 = threading.Thread(target=countNum,args=(34,))

    t1.start() #启动线程
    t2.start()

    print("ending!")
继承Thread式创建:
import threading
import time

class MyThread(threading.Thread):

    def __init__(self,num):
        threading.Thread.__init__(self)
        self.num=num

    def run(self):
        print("running on number:%s" %self.num)
        time.sleep(3)

t1=MyThread(56)
t2=MyThread(78)

t1.start()
t2.start()
print("ending")
join()方法和setDaemon()方法
# join():在子线程完成运行之前,这个子线程的父线程将一直被阻塞。

# setDaemon(True):
'''
         将线程声明为守护线程,必须在start() 方法调用之前设置,如果不设置为守护线程程序会被无限挂起。

         当我们在程序运行中,执行一个主线程,如果主线程又创建一个子线程,主线程和子线程 就分兵两路,分别运行,那么当主线程完成

         想退出时,会检验子线程是否完成。如果子线程未完成,则主线程会等待子线程完成后再退出。但是有时候我们需要的是只要主线程

         完成了,不管子线程是否完成,都要和主线程一起退出,这时就可以 用setDaemon方法啦
'''


import threading
from time import ctime,sleep
import time

def Music(name):

        print ("Begin listening to {name}. {time}".format(name=name,time=ctime()))
        sleep(3)
        print("end listening {time}".format(time=ctime()))

def Blog(title):

        print ("Begin recording the {title}. {time}".format(title=title,time=ctime()))
        sleep(5)
        print('end recording {time}'.format(time=ctime()))


threads = []


t1 = threading.Thread(target=Music,args=('FILL ME',))
t2 = threading.Thread(target=Blog,args=('',))

threads.append(t1)
threads.append(t2)

if __name__ == '__main__':

    #t2.setDaemon(True)

    for t in threads:

        #t.setDaemon(True) #注意:一定在start之前设置
        t.start()

        #t.join()

    #t1.join()
    #t2.join()    #  考虑这三种join位置下的结果?

    print ("all over %s" %ctime())
其他方法:
Thread实例对象的方法
  # isAlive(): 返回线程是否活动的。
  # getName(): 返回线程名。
  # setName(): 设置线程名。

threading模块提供的一些方法:
  # threading.currentThread(): 返回当前的线程变量。
  # threading.enumerate(): 返回一个包含正在运行的线程的list。正在运行指线程启动后、结束前,不包括启动前和终止后的线程。
  # threading.activeCount(): 返回正在运行的线程数量,与len(threading.enumerate())有相同的结果。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值