Python零基础入门--基础(十四)--进程、线程、协程

一、进程(process)

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

在多道编程中,我们允许多个程序同时加载到内存中,在操作系统的调度下,可以实现并发地执行。这是这样的设计,大大提高了CPU的利用率。进程的出现让每个用户感觉到自己独享CPU,因此,进程就是为了在CPU上实现多道编程而提出的。

 

二、线程(thread)

        线程是操作系统能够进行运算调度的最小单位。它被包含在进程之中,是进程中的实际运作单位。一条线程指的是进程中一个单一顺序的控制流,一个进程中可以并发多个线程,每条线程并行执行不同的任务。由于任何进程默认就会启动一个线程。该线程称为主线程,主线程又可以启动新的线程。

 

三、Python threading模块

        Python的threading模块有个current_thread()函数,它永远返回当前线程的实例。主线程实例的名字叫MainThread,子线程的名字在创建时指定,我们用ClildThread命名子线程。名字仅仅在打印时用来显示,完全没有其他意义,如果不起名字Python就自动给线程命名为Thread-1Thread-2……

3.1 主线程的任务完成之后,主线程随之结束,子线程继续执行自己的任务,直到全部的子线程的任务全部结束,程序结束

 

        使用Threading模块创建线程,直接从threading.Thread继承,然后重写__init__方法和run方法:

#/usr/bin/python
#-*- coding:UTF-8 -*-


import threading
import time,datetime
 
 
class MyThread(threading.Thread):
    def __init__(self,num,name):
        threading.Thread.__init__(self)
        self.num = num
        self.name = name
        
 
    def run(self):#定义每个线程要运行的函数
    
        print ('thread %s start.'%threading.current_thread().name) #子线程
        
        print("start %s running on number:%s-%s" %(threading.current_thread().name,self.num,datetime.datetime.now()))
 
        time.sleep(30)
        
        print("End %s running on number:%s-%s" %(threading.current_thread().name,self.num,datetime.datetime.now()))

def main():
    
    print ('thread %s start.'%threading.current_thread().name) #主线程
    
    print("start main:%s " %(datetime.datetime.now()))
    
    t1 = MyThread(1, name='ChlidThread1')
    t2 = MyThread(2, name='ChlidThread2')
    
    
    for t in [t1,t2]:
        
        t.start()  
    
    
if __name__ == '__main__':
    
    print("start %s :%s" %(threading.current_thread().name,datetime.datetime.now()))
    
    main()
    
    print ('thread %s ended.'%threading.current_thread().name)  #任何进程默认就会启动一个线程,我们把该线程称为主线程,主线程介绍
    
    print("end main:%s " %(datetime.datetime.now()))  # 

执行结果:

start MainThread :2019-07-17 17:14:40.885305
thread MainThread start.
start main:2019-07-17 17:14:40.885356 
thread ChlidThread1 start.
thread ChlidThread2 start.
thread MainThread ended.
start ChlidThread1 running on number:1-2019-07-17 17:14:40.885785
end main:2019-07-17 17:14:40.885931 
start ChlidThread2 running on number:2-2019-07-17 17:14:40.885888
End ChlidThread1 running on number:1-2019-07-17 17:15:10.916107
End ChlidThread2 running on number:2-2019-07-17 17:15:10.916199

 

3.2 设置守护线程

       当我们使用setDaemon(True)方法,设置子线程为守护线程时,主线程一旦执行结束,则全部线程全部被终止执行,此时子线程的任务还没有完全执行结束,就被迫停止。使用setDaemon()这样做的意义在于:避免子线程无限死循环,导致退不出程序。 

#/usr/bin/python
#-*- coding:UTF-8 -*-


import threading
import time,datetime
 
 
class MyThread(threading.Thread):
    def __init__(self,num,name):
        threading.Thread.__init__(self)
        self.num = num
        self.name = name
        
 
    def run(self):#定义每个线程要运行的函数
    
        print ('thread %s start.'%threading.current_thread().name) #子线程
        
        print("start %s running on number:%s-%s" %(threading.current_thread().name,self.num,datetime.datetime.now()))
 
        time.sleep(30)
        
        print("End %s running on number:%s-%s" %(threading.current_thread().name,self.num,datetime.datetime.now()))

def main():
    
    print ('thread %s start.'%threading.current_thread().name) #主线程
    
    print("start main:%s " %(datetime.datetime.now()))
    
    t1 = MyThread(1, name='ChlidThread1')
    t2 = MyThread(2, name='ChlidThread2')
    
    
    for t in [t1,t2]:
        
        t.setDaemon(True) #设置守护线程
        
        t.start()  
    
    
if __name__ == '__main__':
    
    print("start %s :%s" %(threading.current_thread().name,datetime.datetime.now()))
    
    main()
    
    print ('thread %s ended.'%threading.current_thread().name)  #任何进程默认就会启动一个线程,我们把该线程称为主线程,主线程介绍
    
    print("end main:%s " %(datetime.datetime.now()))  # 

执行过程:

start MainThread :2019-07-17 17:16:01.909362
thread MainThread start.
start main:2019-07-17 17:16:01.909436 
thread ChlidThread1 start.
start ChlidThread1 running on number:1-2019-07-17 17:16:01.909748
thread ChlidThread2 start.
thread MainThread ended.
start ChlidThread2 running on number:2-2019-07-17 17:16:01.910029
end main:2019-07-17 17:16:01.910142

3.3 设置Join

         join所完成的工作就是线程同步,即主线程任务结束之后,进入阻塞状态,一直等待其他的子线程执行结束之后,主线程再终止。主线程A中,创建了子线程B,并且在主线程A中调用了B.join()方法,那么主线程A会在调用的地方等待,直到子线程B完成操作后,才可以接着往下执行。用法:join[timeout]

#/usr/bin/python
#-*- coding:UTF-8 -*-


import threading
import time,datetime
 
 
class MyThread(threading.Thread):
    def __init__(self,num,name):
        threading.Thread.__init__(self)
        self.num = num
        self.name = name
        
 
    def run(self):#定义每个线程要运行的函数
    
        print ('thread %s start.'%threading.current_thread().name) #子线程
        
        print("start %s running on number:%s-%s" %(threading.current_thread().name,self.num,datetime.datetime.now()))
 
        time.sleep(30)
        
        print("End %s running on number:%s-%s" %(threading.current_thread().name,self.num,datetime.datetime.now()))

def main():
    
    print ('thread %s start.'%threading.current_thread().name) #主线程
    
    print("start main:%s " %(datetime.datetime.now()))
    
    t1 = MyThread(1, name='ChlidThread1')
    t2 = MyThread(2, name='ChlidThread2')
    
    
    for t in [t1,t2]:
        
        t.setDaemon(True) #设置守护线程
        
        t.start()  
    
    for t in [t1,t2]: 
        
        t.join()    #设置JOin
    
    
if __name__ == '__main__':
    
    print("start %s :%s" %(threading.current_thread().name,datetime.datetime.now()))
    
    main()
    
    print ('thread %s ended.'%threading.current_thread().name)  #任何进程默认就会启动一个线程,我们把该线程称为主线程,主线程介绍
    
    print("end main:%s " %(datetime.datetime.now()))  # 

执行过程:

start MainThread :2019-07-17 17:16:49.591498
thread MainThread start.
start main:2019-07-17 17:16:49.591547 
thread ChlidThread1 start.
start ChlidThread1 running on number:1-2019-07-17 17:16:49.591728
thread ChlidThread2 start.
start ChlidThread2 running on number:2-2019-07-17 17:16:49.591959
End ChlidThread1 running on number:1-2019-07-17 17:17:19.621878
End ChlidThread2 running on number:2-2019-07-17 17:17:19.622073
thread MainThread ended.
end main:2019-07-17 17:17:19.622155

四、Lock

        多线程和多进程最大的不同在于,多进程中,同一个变量,各自有一份拷贝存在于每个进程中,互不影响,而多线程中,所有变量都由所有线程共享,所以,任何一个变量都可以被任何一个线程修改,因此,线程之间共享数据最大的危险在于多个线程同时改一个变量。

balance = 0
lock = threading.Lock()

def run_thread(n):
    for i in range(100000):
        # 先要获取锁:
        lock.acquire()
        try:
            # 放心地改吧:
            change_it(n)
        finally:
            # 改完了一定要释放锁:
            lock.release()

        当多个线程同时执行lock.acquire()时,只有一个线程能成功地获取锁,然后继续执行代码,其他线程就继续等待直到获得锁为止。

        获得锁的线程用完后一定要释放锁,否则那些苦苦等待锁的线程将永远等待下去,成为死线程。所以我们用try...finally来确保锁一定会被释放。

        锁的好处就是确保了某段关键代码只能由一个线程从头到尾完整地执行,坏处当然也很多,首先是阻止了多线程并发执行,包含锁的某段代码实际上只能以单线程模式执行,效率就大大地下降了。其次,由于可以存在多个锁,不同的线程持有不同的锁,并试图获取对方持有的锁时,可能会造成死锁,导致多个线程全部挂起,既不能执行,也无法结束,只能靠操作系统强制终止。

五、多核CPU

        Python的线程虽然是真正的线程,但解释器执行代码时,有一个GIL锁:Global Interpreter Lock,任何Python线程执行前,必须先获得GIL锁,然后,每执行100条字节码,解释器就自动释放GIL锁,让别的线程有机会执行。这个GIL全局锁实际上把所有线程的执行代码都给上了锁,所以,多线程在Python中只能交替执行,即使100个线程跑在100核CPU上,也只能用到1个核。

        GIL是Python解释器设计的历史遗留问题,通常我们用的解释器是官方实现的CPython,要真正利用多核,除非重写一个不带GIL的解释器。所以,在Python中,可以使用多线程,但不要指望能有效利用多核。如果一定要通过多线程利用多核,那只能通过C扩展来实现,不过这样就失去了Python简单易用的特点。不过,也不用过于担心,Python虽然不能利用多线程实现多核任务,但可以通过多进程实现多核任务。多个Python进程有各自独立的GIL锁,互不影响。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值