day36网络编程——threading模块线程

进程和线程的关系,一个进程内可以含有很多的线程,在Cpython里面,每个进程内多个线程只能用一个core去执行,这样为什么在学了进程之后为什么会学线程的原因。以后可以根据需求去编制多线程的代码和多进程的代码。

1、同一进程内的多个线程是共享该进程的资源

2、创建新的线程开销要远远小于开启新的进程

from threading import Thread
import os,time
def work():
    print("%s is running"%os.getpid())
if __name__=="__main__":
    for i in range(10):
        t1 = Thread(target= work)
        t1.start()
    print("主线程",os.getpid())
简单的线程代码
from threading import Thread
a = 100
def work():
    global a
    a =0
if __name__ =="__main__":
    t = Thread(target=work)
    t.start()
    print(a)
资源共享

 

线程对象的其他方法

Thread实例对象的方法
  # isAlive(): 返回线程是否活动的。
  # getName(): 返回线程名。
  # setName(): 设置线程名。

threading模块提供的一些方法:
  # threading.currentThread(): 返回当前的线程变量。
  # threading.enumerate(): 返回一个包含正在运行的线程的list。正在运行指线程启动后、结束前,不包括启动前和终止后的线程。
  # threading.activeCount(): 返回正在运行的线程数量,与len(threading.enumerate())有相同的结果。
from threading import Thread
import threading
from multiprocessing import Process
import os

def work():
    import time
    time.sleep(3)
    print(threading.current_thread().getName())


if __name__ == '__main__':
    #在主进程下开启线程
    t=Thread(target=work)
    t.start()

    print(threading.current_thread().getName())
    print(threading.current_thread()) #主线程
    print(threading.enumerate()) #连同主线程在内有两个运行的线程
    print(threading.active_count())
    print('主线程/主进程')

    '''
    打印结果:
    MainThread
    <_MainThread(MainThread, started 140735268892672)>
    [<_MainThread(MainThread, started 140735268892672)>, <Thread(Thread-1, started 123145307557888)>]
    主线程/主进程
    Thread-1
    '''

 

线程守护

线程守护用法和进程守护的区别:

守护进程在主进程代码执行完毕后也会停止执行,但是主程序不会结束,直到其他非守护子进程执行完毕,为子进程收尸。(随主)

守护线程在主线程代码执行完毕后也不会停止执行,而是等到其他非守护线程的执行完毕而停止执行。(随非守护)

 

 

 

 

 

 

GIL 全局解释器锁

三个需要注意的点:
#1.线程抢的是GIL锁,GIL锁相当于执行权限,拿到执行权限后才能拿到互斥锁Lock,其他线程也可以抢到GIL,但如果发现Lock仍然没有被释放则阻塞,即便是拿到执行权限GIL也要立刻交出来

#2.join是等待所有,即整体串行,而锁只是锁住修改共享数据的部分,即部分串行,要想保证数据安全的根本原理在于让并发变成串行,join与互斥锁都可以实现,毫无疑问,互斥锁的部分串行效率要更高

#3. 一定要看本小节最后的GIL与互斥锁的经典分析

GIL VS Lock

    机智的同学可能会问到这个问题,就是既然你之前说过了,Python已经有一个GIL来保证同一时间只能有一个线程来执行了,为什么这里还需要lock? 

 首先我们需要达成共识:锁的目的是为了保护共享的数据,同一时间只能有一个线程来修改共享的数据

    然后,我们可以得出结论:保护不同的数据就应该加不同的锁。

 最后,问题就很明朗了,GIL 与Lock是两把锁,保护的数据不一样,前者是解释器级别的(当然保护的就是解释器级别的数据,比如垃圾回收的数据),后者是保护用户自己开发的应用程序的数据,很明显GIL不负责这件事,只能用户自定义加锁处理,即Lock

过程分析:所有线程抢的是GIL锁,或者说所有线程抢的是执行权限

  线程1抢到GIL锁,拿到执行权限,开始执行,然后加了一把Lock,还没有执行完毕,即线程1还未释放Lock,有可能线程2抢到GIL锁,开始执行,执行过程中发现Lock还没有被线程1释放,于是线程2进入阻塞,被夺走执行权限,有可能线程1拿到GIL,然后正常执行到释放Lock。。。这就导致了串行运行的效果

  既然是串行,那我们执行

  t1.start()

  t1.join

  t2.start()

  t2.join()

  这也是串行执行啊,为何还要加Lock呢,需知join是等待t1所有的代码执行完,相当于锁住了t1的所有代码,而Lock只是锁住一部分操作共享数据的代码。

from threading import Thread
from multiprocessing import Lock
import time
n =100
def work():
    global n
    with lock:
        temp = n
        time.sleep(0.01)
        n -=1
if __name__ == '__main__':
    lock = Lock()
    t_l =[]
    for i in range(100):
        t =  Thread(target=work)
        t_l.append(t)
        t.start()
    for i in t_l:
        i.join()
    print("主线程",n)

基于多线程的并发,cpython解释器不允许并行的这用情况,我们可以根据具体需要选择使用多进程和多线程。

多线程的优势在于创建新的线程开销要远远小于开启新的进程。所以能使用多线程就去尽量使用。

如果是i/o操作较多的时候就尽量使用开线程的方式,比起开进程省去了创建新进程的时间。

如果是纯计算操作较多的时候尽量使用开进程的方式,多核操作比起多线程的串行操作省很多时间。

from multiprocessing import Process
from threading import Thread
import os,time
def work():
     res=0
     for i in range(100000000):
         res*=i

if __name__ == '__main__':
    l=[]
    start=time.time()
    for i in range(400):
        # p=Process(target=work) # 5.158523797988892
        p=Thread(target=work) #20.0011146068573
        l.append(p)
        p.start()
    for p in l:
        p.join()
    stop=time.time()
    print('run time is %s' %(stop-start))
纯计算开进程快
from multiprocessing import Process
from threading import Thread
import os,time
def work():
    time.sleep(2)

if __name__ == '__main__':
    l=[]
    start=time.time()
    for i in range(4):
        p=Process(target=work) # 2.4321393966674805
        # p=Thread(target=work) #2.0011146068573
        l.append(p)
        p.start()
    for p in l:
        p.join()
    stop=time.time()
    print('run time is %s' %(stop-start))
i/o操作较多时开线程

 

posted @ 2017-10-13 20:03  hello沃德  阅读(97)  评论(0)    收藏  举报