python-day34--并发编程之多线程

理论部分

一、什么是线程:

1.线程:一条流水线的工作过程 

2.一个进程里至少有一个线程,这个线程叫主线程

  进程里真正干活的就是线程

3.进程只是用来把资源集中到一起(进程只是一个资源单位,或者说资源集合),而线程才是cpu上的执行单位。

4.多线程(即多个控制线程)的概念是,在一个进程中存在多个控制线程,多个控制线程共享该进程的地址空间,相当于一个车间内有多条流水线,都共用一个车间的资源。

  共享就存在竞争,----加锁-----队列

 1 from multiprocessing import Process
 2 from threading import Thread
 3 import os
 4 import time
 5 n=100
 6 def work():
 7     global n
 8     n-=100
 9 
10 if __name__ == '__main__':
11     p=Thread(target=work,)
12     p.start()
13     p.join()
14     print('',n)
15 
16 #结果是0
View Code

二、线程的创建开销小,因而创建线程的速度快

 1 from multiprocessing import Process
 2 from threading import Thread
 3 import os
 4 import time
 5 def work():
 6     print('<%s> is running' %os.getpid())
 7     time.sleep(2)
 8     print('<%s> is done' %os.getpid())
 9 
10 if __name__ == '__main__':
11     t=Thread(target=work,)
12     t.start()
13     print('',os.getpid())
14 
15 
16 结果:
17 <20348> is running
18 主 20348
19 <20348> is done
View Code

1.进程之间内存空间是互相隔离的

三、线程与进程的区别

  1.线程共享创建它的进程的地址空间;进程有自己的地址空间。

  2.线程可以直接访问其进程的数据段;进程有自己的父进程数据段的副本。(Linux系统,Windows系统也可以这么说,但是要注意Windows没有子进程的概念)

  3.线程可以与进程的其他线程直接通信;进程必须使用进程间通信来与同级进程通信。

  4.新线程很容易创建;新进程需要父进程的重复。

总之记住两点就ok: 1、多线程共享它们进程的资源,2、线程的创建开销小

 1 from multiprocessing import Process
 2 from threading import Thread
 3 import os
 4 import time
 5 n=100
 6 def work():
 7     global n
 8     n-=100
 9 
10 if __name__ == '__main__':
11     # p=Process(target=work,)
12     p=Thread(target=work,)
13     p.start()
14     p.join()
15     print('',n)
View Code

  5.扩展:分布式与集中式(所有的东西放在一起)

四、为什么实用多线程:

  1、多线程共享它们进程的资源,2、线程的创建开销小

五、多线程的应用举例

  开启一个字处理软件进程,该进程肯定需要办不止一件事情,比如监听键盘输入,处理文字,定时自动将文字保存到硬盘,这三个任务操作的都是同一块数据,因而不能用多进程。只能在一个进程里并发地开启三个线程,如果是单线程,那就只能是,键盘输入时,不能处理文字和自动保存,自动保存时又不能输入和处理文字。 

 

代码部分

六、开启进程的两种方式:

 1 #方式一
 2 from threading import Thread
 3 import time
 4 def sayhi(name):
 5     time.sleep(2)
 6     print('%s say hello' %name)
 7 
 8 if __name__ == '__main__':
 9     t=Thread(target=sayhi,args=('egon',))
10     t.start()
11     print('主线程')
方式一
 1 #方式二
 2 from threading import Thread
 3 import time
 4 class Sayhi(Thread):
 5     def __init__(self,name):
 6         super().__init__()
 7         self.name=name
 8     def run(self):
 9         time.sleep(2)
10         print('%s say hello' % self.name)
11 
12 
13 if __name__ == '__main__':
14     t = Sayhi('egon')
15     t.start()
16     print('主线程')
方式二

七、多线程共享同一个进程内的地址空间

  多线程共享——>竞争——>锁

八、练习

from socket import *
from threading import Thread
s=socket(AF_INET,SOCK_STREAM)
s.setsockopt(SOL_SOCKET,SO_REUSEADDR,1)
s.bind(('192.168.20.95',8082))
s.listen(5)
def talk(conn):
    while True:
        try:
            cmd=conn.recv(1024)
            if not cmd:break
            conn.send(cmd.upper())
        except Exception:
            break
    conn.close()

if __name__ == '__main__':
    while True:
        conn,addr=s.accept()
        p=Thread(target=talk,args=(conn,))
        p.start()
    s.close()
多线程并发的socket服务端
 1 from socket import *
 2 c=socket(AF_INET,SOCK_STREAM)
 3 c.connect(('192.168.20.95',8082))
 4 
 5 while True:
 6     msg=input('>>: ').strip()
 7     if not msg:continue
 8     c.send(msg.encode('utf-8'))
 9     data=c.recv(1024)
10     print(data.decode('utf-8'))
11 c.close()
客户端

练习二:三个任务,一个接收用户输入,一个将用户输入的内容格式化成大写,一个将格式化后的结果存入文件

 1 from threading import Thread
 2 input_l=[]
 3 format_l=[]
 4 def talk():
 5     while True:
 6         msg=input('>>: ')
 7         if not msg:continue
 8         input_l.append(msg)
 9 def format():
10     while True:
11         if input_l:
12             res=input_l.pop()
13             format_l.append(res.upper())
14 def save():
15     with open('db.txt','a') as f:
16         while True:
17             if format_l:
18                 f.write('%s\n' %(format_l.pop()))
19                 f.flush()
20 
21 
22 if __name__ == '__main__':
23     t1=Thread(target=talk)
24     t2=Thread(target=format)
25     t3=Thread(target=save)
26 
27     t1.start()
28     t2.start()
29     t3.start()
View Code

九、线程相关的其他属性及方法(了解)

1 Thread实例对象的方法
2   # isAlive(): 返回线程是否活动的。
3   # getName(): 返回线程名。
4   # setName(): 设置线程名。
5 
6 threading模块提供的一些方法:
7   # threading.currentThread(): 返回当前的线程变量。
8   # threading.enumerate(): 返回一个包含正在运行的线程的list。正在运行指线程启动后、结束前,不包括启动前和终止后的线程。
9   # threading.activeCount(): 返回正在运行的线程数量,与len(threading.enumerate())有相同的结果。
View Code
 1 from threading import Thread,currentThread,activeCount
 2 import os,time,threading
 3 def talk():
 4     print('%s is running' %currentThread().getName())
 5 
 6 if __name__ == '__main__':
 7     # t=Thread(target=talk,name='egon')
 8     t=Thread(target=talk)
 9     t.start()
10     print(t.name)
11     print(t.getName())
12     print(t.is_alive())
13     print(currentThread().getName())
14     print(threading.enumerate())
15     print('',activeCount())
16 
17 
18 
19 #结果
20 Thread-1 is running
21 Thread-1
22 Thread-1
23 False
24 MainThread
25 [<_MainThread(MainThread, started 33752)>]
26 主 1
View Code

十、守护进程

  进程: 主进程代码完——>守护死

  线程: 主线程的非守护线程完——>守护死

十一、全局解释器锁:(GIL——CPython解释器的)

  1.保护系统级别的(CPython解释器)代码的安全,但是不能保护用户级别的数据(软件产生的代码)安全

  2.抢锁: 所有线程抢的是GIL锁,或者说所有线程抢的是执行权限

  3.释放锁: ①I/O阻塞  ②运行时间过长

  4.在一个进程中多个线程通过GIL就会变成串行的效果

  5.有了GIL的存在,同一时刻同一进程中只有一个线程被执行

  6.应用:

    多线程用于IO密集型,如socket,爬虫,web
    多进程用于计算密集型,如金融分析

十二、同步锁:

  GIL VS 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只是锁住一部分操作共享数据的代码。

1 分析:
2   #1.100个线程去抢GIL锁,即抢执行权限
3      #2. 肯定有一个线程先抢到GIL(暂且称为线程1),然后开始执行,一旦执行就会拿到lock.acquire()
4      #3. 极有可能线程1还未运行完毕,就有另外一个线程2抢到GIL,然后开始运行,但线程2发现互斥锁lock还未被线程1释放,于是阻塞,被迫交出执行权限,即释放GIL
5     #4.直到线程1重新抢到GIL,开始从上次暂停的位置继续执行,直到正常释放互斥锁lock,然后其他的线程再重复2 3 4的过程
GIL锁与互斥锁综合分析(重点!!!)

 练习:

 1 from threading import Thread,Lock
 2 import time
 3 n=100
 4 def work():
 5     mutex.acquire()
 6     global n
 7     temp=n
 8     time.sleep(0.1)
 9     n=temp-1
10     mutex.release()
11 
12 if __name__ == '__main__':
13     mutex=Lock()
14     t_l=[]
15     for i in range(100):
16         t=Thread(target=work)
17         t_l.append(t)
18         t.start()
19     for t in t_l:
20         t.join()
21     print(n)
View Code

 

  

    

 

posted @ 2017-08-28 17:31  Cool·  阅读(201)  评论(0编辑  收藏  举报