python之进程,线程

 

程序并不能单独运行,只有将程序装载到内存中,系统为它分配资源才能运行,而这种执行的程序就称之为进程。程序和进程的区别就在于:程序是指令的集合,它是进程运行的静态描述文本;进程是程序的一次执行活动,属于动态概念。java

在多道编程中,咱们容许多个程序同时加载到内存中,在操做系统的调度下,能够实现并发地执行。这是这样的设计,大大提升了CPU的利用率。进程的出现让每一个用户感受到本身独享CPU,所以,进程就是为了在CPU上实现多道编程而提出的python

有了进程为何还要线程?

进程有不少优势,它提供了多道编程,让咱们感受咱们每一个人都拥有本身的CPU和其余资源,能够提升计算机的利用率。不少人就不理解了,既然进程这么优秀,为何还要线程呢?其实,仔细观察就会发现进程仍是有不少缺陷的,主要体如今两点上:编程

  • 进程只能在一个时间干一件事,若是想同时干两件事或多件事,进程就无能为力了。多线程

  • 进程在执行的过程当中若是阻塞,例如等待输入,整个进程就会挂起,即便进程中有些工做不依赖于输入的数据,也将没法执行。并发

例如,咱们在使用qq聊天, qq作为一个独立进程若是同一时间只能干一件事,那他如何实如今同一时刻 即能监听键盘输入、又能监听其它人给你发的消息、同时还能把别人发的消息显示在屏幕上呢?你会说,操做系统不是有分时么?但个人亲,分时是指在不一样进程间的分时呀, 即操做系统处理一会你的qq任务,又切换到word文档任务上了,每一个cpu时间片分给你的qq程序时,你的qq仍是只能同时干一件事呀。app

再直白一点, 一个操做系统就像是一个工厂,工厂里面有不少个生产车间,不一样的车间生产不一样的产品,每一个车间就至关于一个进程,且你的工厂又穷,供电不足,同一时间只能给一个车间供电,为了能让全部车间都能同时生产,你的工厂的电工只能给不一样的车间分时供电,可是轮到你的qq车间时,发现只有一个干活的工人,结果生产效率极低,为了解决这个问题,应该怎么办呢?。。。。没错,你确定想到了,就是多加几个工人,让几我的工人并行工做,这每一个工人,就是线程!dom

什么是线程(thread)?

线程是操做系统可以进行运算调度的最小单位。它被包含在进程之中,是进程中的实际运做单位。一条线程指的是进程中一个单一顺序的控制流,一个进程中能够并发多个线程,每条线程并行执行不一样的任务函数

进程想要执行,就必须先生成一个线程,让线程执行,进程只是把资源整合,不能执行ui

全部再同一个进程里的线程是共享同一块内存空间的操作系统

进程与线程的区别?

启动一个线程要比启动进程快,可是运行速度是没有办法比的。

1,线程共享内存空间,进程的内存是独立的

2,两个子进程之间的数据的分开的,不是共享的,同一个进程中的两个线程是共享的数据

3,同一个进程的线程之间能够直接交流,进程之间想通讯必须经过一个中间代理来实现

4,建立新线程很简单,建立新进程须要对其父进程进行一次克隆

5,一个线程能够控制和操做同一进程里的其余线程,可是进程只能操做子进程

 

 

多线程的实际例子:

#--------------------------这是一种多线程的写法,直接调用--------------------
import threading
import time
def run(n):
print('task',n)
time.sleep(2)
t1 = threading.Thread(target=run,args=('t1',)) #target是标题的意思,调用哪一个方法,就写哪一个方法名字,args('t1',)这是传参数
t2 = threading.Thread(target=run,args=('t2',))
t1.start() #启动线程
t2.start()
'''
这两个线程是并行的,一共等2秒钟
'''
#--------------------------这是另一种多线程的写法,继承式写法--------------------
import threading
import time

class MyThreading(threading.Thread):
def __init__(self,n):
threading.Thread.__init__(self)
self.n = n

def run(self): #必需要叫run 定义每一个线程要运行的函数
print('running task',self.n)
time.sleep(2)

t1 = MyThreading('t1')
t2 = MyThreading('t2')
t1.start()
t1.join() #至关于wait,python里面有join
t2.start()
#--------------------------再次理解多线程运行的原理--------------------
import threading
import time

class MyThreading(threading.Thread):
def __init__(self,n,sleep_lime): #这里把父类的构造函数重写了,因此要重构一下,也就是继承一下
threading.Thread.__init__(self) #这里把父类的构造函数重写了,因此要重构一下,也就是继承一下
self.n = n
self.sleep_time = sleep_lime

def run(self): #必需要叫run 定义每一个线程要运行的函数,线程运行的时候,就是调用run,其余的不调用,只是再这种写法里面
print('running task',self.n)
time.sleep(self.sleep_time)
print('task done',self.n)

t1 = MyThreading('t1',2)
t2 = MyThreading('t2',4)
t1.start() #调用了run方法
#t1.join() #至关于wait,python里面有join,等待
t2.start() #调用了run方法
t1.join() #这是t1的线程等待,程序运行从上往下,运行到t1.join()的时候,等t1线程,此时t2线程继续调用run方法运行,t1时间到了后,程序继续从上往下走,运行print('main...........')
#等print('main...........')运行完毕,也意味着主程序(主线程)运行完成,此时t2的等待时间到了以后,就输出t2调用run里面的东西了
#t2.join()
print('main...........',threading.current_thread()) #这个threading.current_thread()是查看这句代码是哪一个线程执行的
print(threading.active_count()) #threading.active_count()这个是查看当前线程活动的个数

#-----------------------------守护进程---------------------------------------------
主进程建立守护进程
其一:守护进程会在主进程代码执行结束后就终止
其二:守护进程内没法再开启子进程,不然抛出异常:AssertionError: daemonic processes are not allowed to have children
注意:进程之间是互相独立的,主进程代码运行结束,守护进程随即终止
import time
def run(n):
print('task',n)
time.sleep(2)
print('task done',n)
#启动50个线程
start_time = time.time()
for i in range(50):
t = threading.Thread(target=run,args=('t-%s'%i,))
t.setDaemon(True) #把当前线程设置为守护线程,必定要再start()以前,程序会等主线程执行完毕,可是不会等守护线程执行完毕就退出
t.start()
print('all threading')
print('cost:',time.time()-start_time)

另一个例子:
from multiprocessing import Process
import time
import random

class Piao(Process):
def __init__(self,name):
self.name=name
super().__init__()
def run(self):
print('%s is piaoing' %self.name)
time.sleep(random.randrange(1,3))
print('%s is piao end' %self.name)


p=Piao('egon')
p.daemon=True #必定要在p.start()前设置,设置p为守护进程,禁止p建立子进程,而且父进程代码执行结束,p即终止运行
p.start()
print('主')


Python GIL(Global Interpreter Lock)全局解析器锁

首先须要明确的一点是GIL并非Python的特性,它是在实现Python解析器(CPython)时所引入的一个概念。就比如C++是一套语言(语法)标准,可是能够用不一样的编译器来编译成可执行代码。有名的编译器例如GCC,INTEL C++,Visual C++等。Python也同样,一样一段代码能够经过CPython,PyPy,Psyco等不一样的Python执行环境来执行。像其中的JPython就没有GIL。然而由于CPython是大部分环境下默认的Python执行环境。因此在不少人的概念里CPython就是Python,也就想固然的把GIL归结为Python语言的缺陷。因此这里要先明确一点:GIL并非Python的特性,Python彻底能够不依赖于GIL

这篇文章透彻的剖析了GIL对python多线程的影响,强烈推荐看一下:http://www.dabeaz.com/python/UnderstandingGIL.pdf 

GIL:多核的意义就是同时作不少事情,可是python中不是这样的,不管你有几核,python运行起来就是单个线程运行,叫假线程。这就叫全局解析器锁
python其实就是调用操做系统原生的线程接口,只能启一个。python调用一个线程的时候,就要把上下文关系传给他。同一时间只能有一个线程去工做,
这是全局锁控制的,java就能自定义执行。之后pypy会是重点,由于取消了这个限制,并且仍是计时编译。

#----------------------------线程锁(互斥锁Mutex)--------------------------
#一个进程下能够启动多个线程,多个线程共享父进程的内存空间,也就意味着每一个线程能够访问同一份数据,此时,若是2个线程同时要修改同一份数据,会出现什么情况?
import time
import threading
def addNum():
global num # 在每一个线程中都获取这个全局变量
print('--get num:', num)
time.sleep(1)
num -= 1 # 对此公共变量进行-1操做
num = 100 # 设定一个共享变量
thread_list = []
for i in range(100):
t = threading.Thread(target=addNum)
t.start()
thread_list.append(t)
for t in thread_list: # 等待全部线程执行完毕
t.join()
print('final num:', num)
正常来说,这个num结果应该是0, 但在python 2.7上多运行几回,会发现,最后打印出来的num结果不老是0,为何每次运行的结果不同呢?
哈,很简单,假设你有A,B两个线程,此时都 要对num 进行减1操做, 因为2个线程是并发同时运行的,因此2个线程颇有可能同时拿走了num=100这个初始变量交给cpu去运算,
当A线程去处完的结果是99,但此时B线程运算完的结果也是99,两个线程同时CPU运算的结果再赋值给num变量后,结果就都是99。那怎么办呢? 很简单,每一个线程在要修改公共数据时,
为了不本身在还没改完的时候别人也来修改此数据,能够给这个数据加一把锁, 这样其它线程想修改此数据时就必须等待你修改完毕并把锁释放掉后才能再访问此数据。
*注:不要在3.x上运行,不知为何,3.x上的结果老是正确的,多是自动加了锁
import time
import threading
def addNum():
global num # 在每一个线程中都获取这个全局变量
print('--get num:', num)
time.sleep(1)
lock.acquire() # 修改数据前加锁
num -= 1 # 对此公共变量进行-1操做
lock.release() # 修改后释放
num = 100 # 设定一个共享变量
thread_list = []
lock = threading.Lock() # 生成全局锁
for i in range(100):
t = threading.Thread(target=addNum)
t.start()
thread_list.append(t)

for t in thread_list: # 等待全部线程执行完毕
t.join()

print('final num:', num)
 
#-----------------------加锁版本--------------------------import threadingimport timedef addNUM():    global num #在每一个线程中都获取这个全局变量    print('--get num:',num)    time.sleep(1)    lock.acquire() #修改数据前加锁    num -=1 #对此公共变量进行-1操做    lock.release()#修改后释放num = 100thread_list = []lock = threading.Lock() #生成全局锁for i in range(100):    t = threading.Thread(target=addNum)    t.start()    thread_list.append(t)for t in thread_list:    t.join()print('num:',num)那你又问了, 既然用户程序已经本身有锁了,那为何C python还须要GIL呢?加入GIL主要的缘由是为了下降程序的开发的复杂度,好比如今的你写python不须要关心内存回收的问题,由于Python解释器帮你自动按期进行内存回收,你能够理解为python解释器里有一个独立的线程,每过一段时间它起wake up作一次全局轮询看看哪些内存数据是能够被清空的,此时你本身的程序 里的线程和 py解释器本身的线程是并发运行的,假设你的线程删除了一个变量,py解释器的垃圾回收线程在清空这个变量的过程当中的clearing时刻,可能一个其它线程正好又从新给这个还没来及得清空的内存空间赋值了, 结果就有可能新赋值的数据被删除了,为了解决相似的问题,python解释器简单粗暴的加了锁,即当一个线程运行时,其它人都不能动,这样就解决了上述的问题,  这能够说是Python早期版本的遗留问题。
相关文章
相关标签/搜索