【python】第四模塊:網絡編程進階&數據庫開發 第1章·網絡編程進階

  • 01-進程與程序的概念
  • 02-操做系統介紹
  • 03-操做系統發展歷史-第一代計算機
  • 04-操做系統發展歷史-批處理系統
  • 05-操做系統發展歷史-多道技術
  • 06-操做系統發展歷史-分時操做系統
  • 07-總結操做系統功能與多道技術
  • 08-進程理論
  • 09-開啓子進程的兩種方式
  • 10-查看進程的pid與ppid
  • 11-殭屍進程與孤兒進程
  • 12-Process對象的其餘屬性或方法
  • 13-練習題講解
  • 14-守護進程
  • 15-互斥鎖
  • 16-模擬搶票
  • 17-互斥鎖與join的區別
  • 18-隊列的使用
  • 19-生產者消費者模型
  • 20-JoinableQueue的使用
  • 21-什麼是線程
  • 22-開啓線程的兩種方式
  • 23-進程與線程的區別
  • 24-Thread對象的其餘屬性或方法
  • 25-守護線程
  • 26-互斥鎖
  • 27-GIL的基本概念
  • 28-GIL與互斥鎖的區別
  • 29-GIL與多線程
  • 30-死鎖與遞歸鎖
  • 31-信號量
  • 32-Event事件
  • 33-定時器
  • 34-線程queue
  • 35-多線程實現併發的套接字通訊
  • 36-進程池線程池
  • 37-異步調用與回調機制
  • 38-進程池線程池小練習
  • 39-協程介紹
  • 40-greenlet模塊
  • 41-greenlet模塊
  • 42-gevent模塊
  • 43-gevent異步提交任務
  • 44-基於gevent模塊實現併發的套接字通訊
  • 45-IO模型介紹
  • 46-阻塞IO模型
  • 47-非阻塞IO模型
  • 48-多路複用IO模型
  • 49-異步IO模型

01-進程與程序的概念

一、進程,即正在執行的一個過程,是一個抽象的概念,起源於操做系統,是操做系統的核心概念,也是操做系統提供的最古老也是最爲重要的抽象概念之一;

二、操做系統的其餘全部內容都是圍繞進程的概念展開的;

02-操做系統介紹

一、操做系統的做用;

  • 隱藏醜陋的硬件接口,提供良好的抽象接口;
  • 管理、調度進程,而且將多個進程對硬件的競爭變得有序;

03-操做系統發展歷史-第一代計算機

一、操做系統發展史;

1)第一代計算機(1940~1955)python

  • 特色:沒有操做系統的概念;也沒有編程語言的概念;全部的程序設計都是直接操做硬件;
  • 優勢:程序員在申請的時間段內獨享整個資源,能夠即時地調試本身的程序(有Bug能夠及時處理)
  • 缺點:浪費計算資源,一個時間段裏只能有一我的用;注意:同一時刻只有一個程序在內存中,被CPU調用執行,比方說10個程序的執行,都是串行的;

04-操做系統發展歷史-批處理系統

一、第二代計算機(1955~1965):晶體管和批處理系統程序員

05-操做系統發展歷史-多道技術

06-操做系統發展歷史-分時操做系統

07-總結操做系統功能與多道技術

08-進程理論

09-開啓子進程的兩種方式

一、multiprocessing模塊介紹;

二、Process類的介紹;

複製代碼
#!/usr/bin/env python3
# -*- coding:utf-8 -*-
# __Author__:TQTL911
# Version:python3.6.6
# Time:7/18/2018 4:14 PM
""
"""
開啓子進程的兩種方式
"""
#開啓進程的方式1:
from multiprocessing import Process
import time

def task(name):
    """
    任務函數
    :param name:
    :return:
    """
    print('%s is running'%name)
    time.sleep(3)
    print('%s is done'%name)

if __name__ == '__main__':
#Process(target= task, kwargs={'name':'子進程1'})
    p = Process(target = task, args = ('子進程1',))
    p.start()#僅僅只是給操做系統發送了一個信號;
    print('主')
"""
主
子進程1 is running
子進程1 is done
"""

"""
開啓進程的方式2:
"""
from multiprocessing import Process
import time

class MyProcess(Process):
    def __init__(self,name):
        super().__init__()
        self.name = name

    def run(self):#默認必須叫作run
        print('%s in running'%self.name)
        time.sleep(3)
        print('%s is done'%self.name)

if __name__ == '__main__':
    p = MyProcess('子進程1')
    p.start()
    print('主')
"""
主
子進程1 in running
子進程1 is done
"""
複製代碼

10-查看進程的pid與ppid

一、pid即process id(進程號)

二、ppid即parent process id(父進程號)

複製代碼
#!/usr/bin/env python3
# -*- coding:utf-8 -*-
# __Author__:TQTL911
# Version:python3.6.6
# Time:7/18/2018 4:52 PM
""
"""
10-查看進程的pid與ppid
操做系統管理進行,使用pid號,相似人類的身份證號;
"""
from multiprocessing import  Process
import time
import os

def task():
    print('%s is running,partent id is <%s>'%(os.getpid(),os.getppid()))
    time.sleep(3)
    print('%s is done,partent id is <%s>'%(os.getpid(),os.getppid()))

if __name__ == '__main__':
    #p = Process(target= task, args=('子進程1'))#TypeError: task() takes 0 positional arguments but 4 were given
    p = Process(target= task,)
    p.start()

    print('主%s,partent id is <%s>'%(os.getpid(),os.getppid()))
"""
主 12040
16924 is running
16924 is done
----------------------
主888,partent id is <14600>
19400 is running,partent id is <888>
19400 is done,partent id is <888>
-----------------------------------
C:\\Users\\TQTL911>tasklist |findstr pycharm
pycharm64.exe                14600 Console                    6    823,488 K

C:\\Users\\TQTL911>
"""
複製代碼

11-殭屍進程與孤兒進程

複製代碼
#!/usr/bin/env python3
# -*- coding:utf-8 -*-
# __Author__:TQTL911
# Version:python3.6.6
# Time:7/18/2018 5:07 PM
""
"""
11-殭屍進程與孤兒進程;
此內容瞭解爲主;
一、殭屍進程,即子進程被殺死後的狀態,方便於父進程查看一些狀態信息,但他是有害的;
二、孤兒進程,即父進程終止後,保存的子進程,但會交由祖宗進程init(全部進程的起點,即孤兒院)去進行管理,它是無害的;
"""
複製代碼

12-Process對象的其餘屬性或方法

一、join方法;

二、Process對象的其餘屬性或方法;terminate與is_alive以及name與pid

複製代碼
#!/usr/bin/env python3
# -*- coding:utf-8 -*-
# __Author__:TQTL911
# Version:python3.6.6
# Time:7/18/2018 5:17 PM
""
"""
12-Process對象的其餘屬性或方法

"""
#一、join方法講解
from multiprocessing import Process
import time
import os
import random

def task():
    print('%s is running,The parent id is <%s>'%(os.getpid(),os.getppid()))
    time.sleep(random.randrange(1,5))
    print('%s is down,The parent id is <%s>'%(os.getpid(),os.getppid()))

if __name__ == '__main__':
    p = Process(target=task,)
    p.start()

    p.join()#保證等待子進程執行結束,父進程才執行
    print('主',os.getpid(),os.getppid())
    print(p.pid)
#二、引入join方法
from multiprocessing import Process
import time
import os
import random

def task(name):
    print('%s is running'%name)
    time.sleep(random.randrange(1,3))

if __name__ == '__main__':
    p1 = Process(target=task,args=('子進程1',))
    p2 = Process(target=task,args=('子進程2',))
    p3 = Process(target=task,args=('子進程3',))
    p4 = Process(target=task,args=('子進程4',))
    p1.start()#只是發個信號給操做系統,至於CPU執行的順序,由操做系統來分配;
    p2.start()
    p3.start()
    p4.start()

    p1.join()#保證等待子進程執行結束,父進程才執行
    p2.join()#保證等待子進程執行結束,父進程才執行
    p3.join()#保證等待子進程執行結束,父進程才執行
    p4.join()#保證等待子進程執行結束,父進程才執行
    print('主',os.getpid(),os.getppid())
"""
一、不曾使用join方法的時候,執行的順序以下所示:
主 14188 14600
子進程1 is running
子進程3 is running
子進程2 is running
子進程4 is running
二、使用了join方法以後,執行的順序以下所示:
子進程1 is running
子進程2 is running
子進程3 is running
子進程4 is running
主 2564 14600
"""

#三、並行執行程序;
from multiprocessing import Process
import os
import random
import time

def task(name,n):
    print('%s is runing'%name,10)
    time.sleep(n)

if __name__ == '__main__':
    start = time.time()
    p1 = Process(target= task, args=('子進程1',5))
    p2 = Process(target= task, args=('子進程2',3))
    p3 = Process(target= task, args=('子進程3',2))

    p1.start()
    p2.start()
    p3.start()

    p1.join()
    p2.join()
    p3.join()
    print('主',(time.time() - start))
"""
子進程1 is runing 10
子進程3 is runing 10
子進程2 is runing 10
主 5.093375205993652    
"""


#四、變成串行方式執行程序;
from multiprocessing import Process
import os
import time

def task(name,n):
    print('%s is running'%name,n)
    time.sleep(n)

if __name__ == '__main__':
    start = time.time()
    # p1 = Process(target= task,args=('子進程1',5),)
    # p2 = Process(target= task,args=('子進程2',1),)
    # p3 = Process(target= task,args=('子進程3',2),)
    #
    # p1.start()
    # p1.join()
    # p2.start()
    # p2.join()
    # p3.start()
    # p3.join()
#對以上內容的簡寫
    p1 = Process(target=task, args=('子進程1', 5), )
    p2 = Process(target=task, args=('子進程2', 1), )
    p3 = Process(target=task, args=('子進程3', 2), )
    p_l = [p1,p2,p3]
    for p in p_l:
        p.start()
    for p in p_l:
        #p.join()#TabError: inconsistent use of tabs and spaces in indentation
        p.join()
#print('主',time.time() - start)#NameError: name 'start' is not defined
    print('主',time.time() - start)
"""
子進程1 is running 5
子進程2 is running 1
子進程3 is running 2
主 8.224236726760864
"""

#小結
from multiprocessing import Process
import os
import time
import random

def task():
    print('%s is runing,The parent id is <%s>'%(os.getpid(),os.getppid()))
    time.sleep(random.randrange(1,3))
    print('%s is done,The parent id is <%s>'%(os.getpid(),os.getppid()))

if __name__ == '__main__':#儘量手寫,不使用main自動生成,會出現tab != 4個空格的坑呀!
    # p = Process(target= task,)
    # p.start()
    # p.join()
    # print('主',os.getpid(),os.getppid())
    # print(p.pid)
    # print(p.is_alive())#布爾值False or True
    p = Process(target=task,)
    p.start()
    p.terminate()
    time.sleep(3)#加上時間後,is_alive將會變成False
    print(p.is_alive())#True
    print('主',os.getpid(),os.getppid())
複製代碼

13-練習題講解

一、改寫下列程序,分別別實現下述打印效果;

複製代碼
#!/usr/bin/env python3
# -*- coding:utf-8 -*-
# __Author__:TQTL911
# Version:python3.6.6
# Time:7/18/2018 6:53 PM
from multiprocessing import Process
import time
import random

def task(n):
    time.sleep(random.randrange(1,3))
    print('--------->%s'%n)

if __name__ == '__main__':
    p1 = Process(target=task,args=(1,))
    p2 = Process(target=task,args=(2,))
    p3 = Process(target=task,args=(3,))
# Req1:最早保證輸出--------->4
    p1.start()
    p2.start()
    p3.start()
    print('最早保證輸出--------->4')

#Req2:保證最後輸出--------->4
    p1.start()
    p2.start()
    p3.start()
    p1.join()
    p2.join()
    p3.join()
    print('保證最後輸出--------->4')

#Req3:保證按順序輸出--------->4
#以下寫,變成串行,失去併發的意義,沒有意義,爲了練習join方法的使用;
    p1.start()
    p1.join()
    p2.start()
    p1.join()
    p3.start()
    p1.join()
    print('保證按順序輸出--------->4')
複製代碼

二、基於多進程實現併發套接字通訊;

1)server端;數據庫

複製代碼
#!/usr/bin/env python3
# -*- coding:utf-8 -*-
# __Author__:TQTL911
# Version:python3.6.6
# Time:7/18/2018 6:37 PM
from socket import *
from multiprocessing import Process

def talk(conn):
    while True:
        try:
            data = conn.recv(1024)
            if not data:break
            conn.send(data.upper())
        except ConnectionResetError:
            break
        conn.close()

def server(ip,port):
    server = socket(AF_INET,SOCK_STREAM)
    server.setsockopt(SOL_SOCKET,SO_REUSEADDR,1)
    server.bind((ip,port))#注意此處爲元組形式的ip_port:(ip,port)
    server.listen(5)

    while True:
        conn,addr = server.accept()
        p = Process(target=talk,args=(conn,))
        p.start()

    server.close()

if __name__ == '__main__':
    server('127.0.0.1',9011)
複製代碼

2)client端;編程

複製代碼
#!/usr/bin/env python3
# -*- coding:utf-8 -*-
# __Author__:TQTL911
# Version:python3.6.6
# Time:7/18/2018 6:37 PM

from socket import *

client = socket(AF_INET,SOCK_STREAM)
client.connect(('127.0.0.1',9011))

while True:
    msg = input('>>>:').strip()
    if not msg:continue

    client.send(msg.encode('utf-8'))
    data = client.recv(1024)
    print(data.decode('utf-8'))
複製代碼

14-守護進程

一、守護進程初識;

  • 守護進程會在主進程代碼執行結束後就終止;
  • 守護進程內沒法再開啓子進程,不然拋出異常;AssertionError: daemonic processes are not allowed to have children
複製代碼
#!/usr/bin/env python3
# -*- coding:utf-8 -*-
# __Author__:TQTL911
# Version:python3.6.6
# Time:7/20/2018 8:26 AM
""
"""
守護進程-伴隨着父進程而消失;
"""
from multiprocessing import Process
import time
import random

def task(name):
    print('%s is running'%name)
    time.sleep(random.randrange(1,3))

    # p = Process(target=time.sleep,args=(3,))
    # p.start()#AssertionError: daemonic processes are not allowed to have children

if __name__ == '__main__':
    p = Process(target=task,args=('子進程1',))
    p.daemon = True
    p.start()

    p.join()
    print('主進程')

#練習題;
from multiprocessing import Process

import time
def foo():
    print(123)
    time.sleep(1)
    print("end123")

def bar():
    print(456)
    time.sleep(3)
    print("end456")

if __name__ == '__main__':
    p1=Process(target=foo)
    p2=Process(target=bar)

    p1.daemon=True#必定要在p.start()前設置,設置p爲守護進程,禁止p建立子進程,而且父進程代碼執行結束,p即終止運行
    p1.start()
    p2.start()
    print("main-------")
"""
main-------#只要碰到main,就不會出現123和end123;
456
end456
"""
複製代碼

15-互斥鎖

一、互斥鎖初識;

複製代碼
#!/usr/bin/env python3
# -*- coding:utf-8 -*-
# __Author__:TQTL911
# Version:python3.6.6
# Time:7/20/2018 8:44 AM
""
"""
互斥鎖-互相排斥,好比生活中去衛生間,關上門,其餘人必須等待;
互斥鎖的原理-把併發改爲串行,下降了效率,可是保證了數據安全不錯亂;
"""
from multiprocessing import Process,Lock
import time
import random

"""
進程之間數據不共享,可是共享同一套文件系統,因此訪問同一個文件,
或同一個打印終端,是沒有問題的,而共享帶來的是競爭,
競爭帶來的結果就是錯亂,以下:
"""
def task(name,mutex):#mutex互斥的意思;
    mutex.acquire()#獲取鎖;
    print('%s 1'%name)
    time.sleep(random.randrange(1,2))
    print('%s 2'%name)
    time.sleep(random.randrange(1,2))
    print('%s 3'%name)
    mutex.release()#釋放鎖;

if __name__ == '__main__':
    mutex = Lock()#生成鎖
    for i in range(3):
        p = Process(target=task,args=('進程%s'%i,mutex))
        p.start()
"""
一、未引入互斥鎖
進程0 1
進程1 1
進程2 1
進程0 2
進程1 2
進程2 2
進程0 3
進程1 3
進程2 3
二、引入互斥鎖
進程0 1
進程0 2
進程0 3
進程1 1
進程1 2
進程1 3
進程2 1
進程2 2
進程2 3
"""
複製代碼

16-模擬搶票

一、初次模擬搶票程序;(多個進程共享同一文件,咱們能夠把文件當數據庫,用多個進程模擬多我的執行搶票任務)

複製代碼
#!/usr/bin/env python3
# -*- coding:utf-8 -*-
# __Author__:TQTL911
# Version:python3.6.6
# Time:7/20/2018 8:55 AM
""
"""
16-模擬搶票
"""
from multiprocessing import Process,Lock
import time
import random
import json

def search(name):
    time.sleep(random.randrange(1,2))
    dic = json.load(open('db.txt','r',encoding='utf-8'))
    print('<%s> 查看到剩餘票數[%s]'%(name,dic['count']))

def get(name):
    time.sleep(random.randrange(1,2))#模擬讀取數據的網絡延遲:
    dic = json.load(open('db.txt','r',encoding = 'utf-8'))
    if dic['count'] > 0:
        dic['count'] -= 1
        time.sleep(2)
        json.dump(dic,open('db.txt','w',encoding='utf-8'))
        print('<%s>購票成功'%name)


def task(name):
    search(name)
    get(name)

if __name__ == '__main__':
    for i in range(10):
        p = Process(target = task,args=('路人%s'%i,))
        p.start()
"""
<路人0> 查看到剩餘票數[1]
<路人1> 查看到剩餘票數[1]
<路人2> 查看到剩餘票數[1]
<路人4> 查看到剩餘票數[1]
<路人3> 查看到剩餘票數[1]
<路人6> 查看到剩餘票數[1]
<路人5> 查看到剩餘票數[1]
<路人7> 查看到剩餘票數[1]
<路人8> 查看到剩餘票數[1]
<路人9> 查看到剩餘票數[1]
<路人0>購票成功
<路人1>購票成功
<路人2>購票成功
<路人4>購票成功
<路人3>購票成功
<路人6>購票成功
<路人5>購票成功
<路人7>購票成功
<路人8>購票成功
<路人9>購票成功
"""

#加鎖處理:購票行爲由併發變成了串行,犧牲了運行效率,但保證了數據安全
from multiprocessing import Process,Lock
import time
import random
import json

def search(name):
    time.sleep(random.randrange(1,2))
    dic = json.load(open('db.txt','r',encoding='utf-8'))
    print('<%s> 查看到剩餘票數[%s]'%(name,dic['count']))

def get(name):
    time.sleep(random.randrange(1,2))#模擬讀取數據的網絡延遲:
    dic = json.load(open('db.txt','r',encoding = 'utf-8'))
    if dic['count'] > 0:
        dic['count'] -= 1
        time.sleep(2)
        json.dump(dic,open('db.txt','w',encoding='utf-8'))
        print('<%s>購票成功'%name)


def task(name,mutex):
    #mutex.acquire()#此處,注意加鎖的順序;
    search(name)
    mutex.acquire()
    get(name)
    mutex.release()
"""
<路人0> 查看到剩餘票數[1]
<路人0>購票成功
<路人1> 查看到剩餘票數[0]
<路人2> 查看到剩餘票數[0]
<路人3> 查看到剩餘票數[0]
<路人4> 查看到剩餘票數[0]
<路人5> 查看到剩餘票數[0]
<路人8> 查看到剩餘票數[0]
<路人7> 查看到剩餘票數[0]
<路人6> 查看到剩餘票數[0]
<路人9> 查看到剩餘票數[0]
"""

if __name__ == '__main__':
    mutex = Lock()
    for i in range(10):
        p = Process(target = task,args=('路人%s'%i,mutex))
        p.start()
"""
<路人2> 查看到剩餘票數[1]
<路人0> 查看到剩餘票數[1]
<路人3> 查看到剩餘票數[1]
<路人7> 查看到剩餘票數[1]
<路人1> 查看到剩餘票數[1]
<路人6> 查看到剩餘票數[1]
<路人5> 查看到剩餘票數[1]
<路人8> 查看到剩餘票數[1]
<路人9> 查看到剩餘票數[1]
<路人4> 查看到剩餘票數[1]
<路人2>購票成功
"""
複製代碼

17-互斥鎖與join的區別

一、互斥鎖與join都可以將併發變成串行,爲什麼還要使用互斥鎖呢?

複製代碼
#!/usr/bin/env python3
# -*- coding:utf-8 -*-
# __Author__:TQTL911
# Version:python3.6.6
# Time:7/20/2018 9:49 AM
from multiprocessing import Process,Lock
import time
import random
import json

def search(name):
    time.sleep(random.randrange(1,2))
    dic = json.load(open('db.txt','r',encoding='utf-8'))
    print('<%s> 查看到剩餘票數[%s]'%(name,dic['count']))

def get(name):
    time.sleep(random.randrange(1,2))#模擬讀取數據的網絡延遲:
    dic = json.load(open('db.txt','r',encoding = 'utf-8'))
    if dic['count'] > 0:
        dic['count'] -= 1
        time.sleep(2)
        json.dump(dic,open('db.txt','w',encoding='utf-8'))
        print('<%s>購票成功'%name)
    else:
        print('<%s>購票失敗'%name)

def task(name,):
    #mutex.acquire()
    search(name)
    #mutex.acquire()
    get(name)
    #mutex.release()
if __name__ == '__main__':
    #mutex = Lock()
    for i in range(10):
        p = Process(target = task,args=('路人%s'%i,))#args傳值爲元組,帶有逗號
        p.start()
        p.join()
"""
<路人0> 查看到剩餘票數[1]
<路人0>購票成功
<路人1> 查看到剩餘票數[0]
<路人1>購票失敗
<路人2> 查看到剩餘票數[0]
<路人2>購票失敗
<路人3> 查看到剩餘票數[0]
<路人3>購票失敗
<路人4> 查看到剩餘票數[0]
<路人4>購票失敗
<路人5> 查看到剩餘票數[0]
<路人5>購票失敗
<路人6> 查看到剩餘票數[0]
<路人6>購票失敗
<路人7> 查看到剩餘票數[0]
<路人7>購票失敗
<路人8> 查看到剩餘票數[0]
<路人8>購票失敗
<路人9> 查看到剩餘票數[0]
<路人9>購票失敗
"""

#小結:
"""
一、發現使用join方法後,將併發改成串行,確實能保證數據安全,但問題連查票操做也
變成只能一個一個去查了,很明顯,你們查票的時候應該是併發地去查詢
而無需考慮數據正確與否,此時join與互斥鎖的區別就顯而易見了。
二、join是將一個任務總體串行,而互斥鎖的好處則是將一個任務中的某一行代碼串行,好比只讓
task函數的get任務串行;


def task(name,):
    search(name)#併發執行
    
    Lock.acquire()
    get(name)#串行執行
    Lock.release()
"""
複製代碼

18-隊列的使用

一、隊列初識;

進程彼此之間互相隔離,要實現進程間通訊(IPC),multiprocessing模塊支持兩種形式:隊列和管道,這兩種方式都是使用消息傳遞的;json

1)建立隊列的類(底層就是以管道和鎖定的方式實現的)緩存

Queue([maxsize]):建立共享的進程隊列,Queue是多進程安全的隊列,可使用Queue實現多進程之間的數據傳遞。

2)Queue的參數介紹;安全

maxsize是隊列中容許最大項數,省略則無大小限制。
但須要明確:
    一、隊列內存放的是消息而非大數據
    二、隊列佔用的是內存空間,於是maxsize即使是無大小限制也受限於內存大小

3)主要方法介紹網絡

q = Queue(n)
q.put方法用以插入數據到隊列中。
q.get方法能夠從隊列讀取而且刪除一個元素。

4)隊列的使用多線程

複製代碼
#!/usr/bin/env python3
# -*- coding:utf-8 -*-
# __Author__:TQTL911
# Version:python3.6.6
# Time:7/20/2018 10:04 AM
""
"""
掌握隊列的使用-先進先出的原則
"""
from multiprocessing import Queue

q = Queue(3)#設置隊列數爲3,注意:隊列中不能放置大文件,而應該是精簡的消息;
q.put('hello')
q.put({'a':1})
q.put([1,2,3,4])
#q.put(4)
print(q.get())
print(q.get())
print(q.get())

print(q.empty())#清空隊列操做;返回值爲True;
#print(q.get())#當隊列中的數據被取空以後,再次get,程序將一直停留在這裏;
複製代碼

19-生產者消費者模型

一、爲何要使用生產者消費者模型?

生產者指的是生產數據的任務,消費者指的是處理數據的任務,在併發編程中,若是生產者處理速度很快,而消費者處理速度很慢,那麼生產者就必須等待消費者處理完,才能繼續生產數據。一樣的道理,若是消費者的處理能力大於生產者,那麼消費者就必須等待生產者。爲了解決這個問題因而引入了生產者和消費者模式。

二、什麼是生產者和消費者模式?

這裏強調:生產者消費者模式是經過一個容器來解決生產者和消費者的強耦合問題。

生產者和消費者彼此之間不直接通信,而經過阻塞隊列來進行通信,因此生產者生產完數據以後不用等待消費者處理,直接扔給阻塞隊列,消費者不找生產者要數據,而是直接從阻塞隊列裏取,阻塞隊列就至關於一個緩衝區,平衡了生產者和消費者的處理能力。

注意:這個阻塞隊列就是用來給生產者和消費者解耦的

三、生產者消費者模型實現;

複製代碼
#!/usr/bin/env python3
# -*- coding:utf-8 -*-
# __Author__:TQTL911
# Version:python3.6.6
# Time:7/20/2018 10:11 AM
""
"""
 19-生產者消費者模型
"""
import time

def producter():
    for i in range(3):
        res = '包子%s'%i
        print('生產者生產了%s'%res)
        consumer(res)


def consumer(res):
    time.sleep(1)
    print('消費者吃了%s'%res)
producter()
"""
生產者生產了包子0
消費者吃了包子0
生產者生產了包子1
消費者吃了包子1
生產者生產了包子2
消費者吃了包子2
"""
"""
#生產者與消費者之間彼此可能等待對方;
#引入生產者消費者模型,在生產者與消費者之間引入一個「容器」,相似於緩存的做用;
#好處之一:解耦合;
#好處之二平衡了消費者和生產者之間的不對等的能力:
"""

from multiprocessing import Process,Queue
import time

def producter(q):
    for i in range(10):
        res = '包子%s'%i
        time.sleep(0.5)#模擬生產消耗的時間
        print('生產者生產了%s'%res)
        q.put(res)

def consumer(q):
    while True:
        res = q.get()
        time.sleep(1)
        print('消費者吃了%s'%res)

if __name__ == '__main__':
    #中間的容器
    q = Queue()
    #生產者
    p1 = Process(target=producter,args=(q,))
    #消費者們
    c1 = Process(target=consumer,args=(q,))
    p1.start()
    c1.start()
    print('主')
"""
主
生產者生產了包子0
生產者生產了包子1
生產者生產了包子2
消費者吃了包子0
生產者生產了包子3
消費者吃了包子1
生產者生產了包子4
生產者生產了包子5
消費者吃了包子2
生產者生產了包子6
生產者生產了包子7
消費者吃了包子3
生產者生產了包子8
生產者生產了包子9
消費者吃了包子4
消費者吃了包子5
消費者吃了包子6
消費者吃了包子7
消費者吃了包子8
消費者吃了包子9

引入了隊列的概念,生產者與消費者之間互不影響;但以上會出現消費者持續等待的情景
"""

"""
生產者生產了包子0
生產者生產了包子1
消費者吃了包子0
生產者生產了包子2
生產者生產了包子3
生產者生產了包子4
消費者吃了包子1
生產者生產了包子5
消費者吃了包子2
生產者生產了包子6
生產者生產了包子7
消費者吃了包子3
生產者生產了包子8
生產者生產了包子9
主
消費者吃了包子4
消費者吃了包子5
消費者吃了包子6
消費者吃了包子7
消費者吃了包子8
消費者吃了包子9

Process finished with exit code 0
"""
#生產者有多個;
from multiprocessing import Process,Queue
import time

def producter(q):
    for i in range(10):
        res = '包子%s'%i
        time.sleep(0.5)#模擬生產消耗的時間
        print('生產者生產了%s'%res)
        q.put(res)

def consumer(q):
    while True:
        res = q.get()
        if res is None:break
        time.sleep(1)
        print('消費者吃了%s'%res)

if __name__ == '__main__':
    #中間的容器
    q = Queue()
    #生產者
    p1 = Process(target=producter,args=(q,))
    p2 = Process(target=producter,args=(q,))
    p3 = Process(target=producter,args=(q,))
    p4 = Process(target=producter,args=(q,))
    #消費者們
    c1 = Process(target=consumer,args=(q,))
    c2 = Process(target=consumer,args=(q,))
    c3 = Process(target=consumer,args=(q,))

    p1.start()
    p2.start()
    p3.start()
    p4.start()
    c1.start()
    c2.start()
    c3.start()


    p1.join()
    p2.join()
    p3.join()
    p4.join()
    q.put(None)
    q.put(None)
    q.put(None)
    print('主')
複製代碼

20-JoinableQueue的使用

一、引入JoinableQueue([maxsize]);

 

這就像是一個Queue對象,但隊列容許項目的使用者通知生成者項目已經被成功處理。通知進程是使用共享的信號和條件變量來實現的。

 

二、JoinableQueue方法介紹;

JoinableQueue的實例p除了與Queue對象相同的方法以外還具備:
q.task_done():使用者使用此方法發出信號,表示q.get()的返回項目已經被處理。若是調用此方法的次數大於從隊列中刪除項目的數量,將引起ValueError異常
q.join():生產者調用此方法進行阻塞,直到隊列中全部的項目均被處理。阻塞將持續到隊列中的每一個項目均調用q.task_done()方法爲止

 

三、基於JoinableQueue實現生產者消費者模型;

複製代碼
#!/usr/bin/env python3
# -*- coding:utf-8 -*-
# __Author__:TQTL911
# Version:python3.6.6
# Time:7/20/2018 11:24 AM
from multiprocessing import Process,JoinableQueue,Queue
import time
import random

def producer(q):
    for i in range(3):
        res = '包子%s'%i
        time.sleep(0.5)
        print('生產了%s'%res)
        q.put(res)
    q.join()
def consumer(q):
    while True:
        res = q.get()
        if res is None:break
        time.sleep(1)
        print('消費者吃了%s'%res)
        q.task_done()

if __name__ == '__main__':
    #容器
    q = JoinableQueue()

    p1 = Process(target=producer,args=(q,))
    p2 = Process(target=producer,args=(q,))
    p3 = Process(target=producer,args=(q,))

    c1 = Process(target=consumer,args=(q,))
    c2 = Process(target=consumer,args=(q,))
    c1.daemon = True
    c2.daemon = True
    p1.start()
    p2.start()
    p3.start()

    c1.start()
    c2.start()

    p1.join()
    p2.join()
    p3.join()
    print('主')
"""
生產了包子0
生產了包子0
生產了包子0
生產了包子1
生產了包子1
生產了包子1
消費者吃了包子0
生產了包子2
消費者吃了包子0
生產了包子2
生產了包子2
消費者吃了包子0
消費者吃了包子1
消費者吃了包子1
消費者吃了包子1
消費者吃了包子2
消費者吃了包子2
消費者吃了包子2
主

Process finished with exit code 0
"""
複製代碼

21-什麼是線程

一、什麼是線程?

在傳統操做系統中,每一個進程有一個地址空間,並且默認就有一個控制線程

線程顧名思義,就是一條流水線工做的過程(流水線的工做須要電源,電源就至關於cpu),而一條流水線必須屬於一個車間,一個車間的工做過程是一個進程,車間負責把資源整合到一塊兒,是一個資源單位,而一個車間內至少有一條流水線。

小結:因此,進程只是用來把資源集中到一塊兒(進程只是一個資源單位,或者說資源集合),而線程纔是cpu上的執行單位。

二、什麼是多線程?

多線程(即多個控制線程)的概念是,在一個進程中存在多個線程,多個線程共享該進程的地址空間,至關於一個車間內有多條流水線,都共用一個車間的資源。例如,北京地鐵與上海地鐵是不一樣的進程,而北京地鐵裏的13號線是一個線程,北京地鐵全部的線路共享北京地鐵全部的資源,好比全部的乘客能夠被全部線路拉。

三、線程與進程的區別?

1) 來點兒英文範兒吧!併發

複製代碼
一、Threads share the address space of the process that created it; processes have their own address space.
二、Threads have direct access to the data segment of its process; processes have their own copy of the data segment of the parent process.
三、Threads can directly communicate with other threads of its process; processes must use interprocess communication to communicate with sibling processes.
四、New threads are easily created; new processes require duplication of the parent process.
五、Threads can exercise considerable control over threads of the same process; processes can only exercise control over child processes.
五、Changes to the main thread (cancellation, priority change, etc.) may affect the behavior of the other threads of the process; changes to the parent process does not affect child processes.
複製代碼

2)算了,仍是別裝13了,直接上中文吧!

  • 同一個進程內的多個線程共享該進程內的地址資源;
  • 建立線程的開銷要遠小於建立進程的開銷(建立一個進程,就是建立一個車間,涉及到申請空間,並且在該空間內建至少一條流水線,但建立線程,就只是在一個車間內造一條流水線,無需申請空間,因此建立開銷小);

四、多線程應用舉例:

一、開啓一個字處理軟件進程,該進程確定須要辦不止一件事情,好比監聽鍵盤輸入,處理文字,定時自動將文字保存到硬盤,這三個任務操做的都是同一塊數據,於是不能用多進程。
二、只能在一個進程裏併發地開啓三個線程,若是是單線程,那就只能是,鍵盤輸入時,不能處理文字和自動保存,自動保存時又不能輸入和處理文字。

 

22-開啓線程的兩種方式

一、threading模塊介紹;

threading與multiprocessing模塊很是類似,具體見後者的用法章節;

二、開啓線程的兩種方式;

複製代碼
#!/usr/bin/env python3
# -*- coding:utf-8 -*-
# __Author__:TQTL911
# Version:python3.6.6
# Time:7/20/2018 11:53 AM
""
"""
22-開啓線程的兩種方式
"""
#回顧開進程的方式;
import time
import random
from multiprocessing import Process

def piao(name):
    print('%s piaoing'%name)
    time.sleep(random.randrange(1,3))
    print('%s piao ended'%name)

if __name__ == '__main__':
    p1 = Process(target=piao,args=('cuixiaozhao',))#必須加上,號
    #調用對象下的方法,開啓四個進程
    p1.start()
    print('主')

#開啓線程的方式1:

import time
import random
from threading import Thread

def piao(name):
    print('%s piaoing'%name)
    time.sleep(random.randrange(1,3))
    print('%s piao ended'%name)

if __name__ == '__main__':
    t1 = Thread(target=piao,args=('cuixiaozhao',))#必須加上,號

    t1.start()
    print('主線程1')

#開啓線程的方式2
from threading import Thread
import time

class MyThread(Thread):
    def __init__(self,name):
        super().__init__()
        self.name = name

    def run(self):#默認必須叫作run
        print('%s in running'%self.name)
        time.sleep(3)
        print('%s is done'%self.name)

if __name__ == '__main__':
    t = MyThread('子線程2')
    t.start()
    print('主線程2')

"""
小結:
一、起線程的兩種方式與起進程的兩種很是很是類似,幾乎如出一轍,就是把multiprocessing換成threading便可
二、明確清楚進程與線程的概念區別:
    同一個進程內的多線線程共享該進程內的地址資源
    建立線程的開銷要遠小於建立進程的開銷(建立一個進程,就是建立一個車間,設計到申請空間,並且在該空間內創建至少與一條流水線(即默認開啓一個線程);
    可是建立線程,就是在一個車間內造出一個新的流水線,無需申請基礎空間,因此建立開銷小)
"""
複製代碼

23-進程與線程的區別

一、進程Process與線程Thread的區別;

複製代碼
#!/usr/bin/env python3
# -*- coding:utf-8 -*-
# __Author__:TQTL911
# Version:python3.6.6
# Time:7/20/2018 12:05 PM
""
"""
23-進程與線程的區別:
一、開進程的開銷遠大於開線程;
二、同一進程內的多個線程共享該進程的地址空間
"""
#區別1;
import time
import random
from threading import Thread
from multiprocessing import Process
def piao(name):
    print('%s piaoing'%name)
    time.sleep(random.randrange(1,3))
    print('%s piao ended'%name)

if __name__ == '__main__':
    # p1 = Process(target=piao, args=('cuixiaozhao',))
    # p1.start()
    """
    主線程1
    cuixiaozhao piaoing
    cuixiaozhao piao ended
    """
    t1 = Thread(target= piao,args=('cuitianqing',))
    t1.start()
    print('主線程1')
    """
    cuitianqing piaoing
    主線程1
    cuitianqing piao ended
    """

#區別2;
from threading import Thread
from multiprocessing import Process

n = 100
def task2():
    global n
    n = 0

if __name__ == '__main__':
    # p1 = Process(target=task2,)
    # p1.start()
    # p1.join()#等待子進程運行完成,結果爲:主線程 100

    t1 = Thread(target= task2,)
    t1.start()#主線程 0
    t1.join()
    print('主線程',n)


#區別3,瞅一眼pid
from threading import Thread
from multiprocessing import Process,current_process
import os

def task3():
    #print(current_process().pid)#7428
    print('子進程PID:%s 父進程的PPID:%s'%(os.getpid(),os.getppid()))#子進程PID:7760 父進程的PPID:19624

if __name__ == '__main__':
    # p1 = Process(target= task3,)
    # p1.start()
    t1 = Thread(target=task1,)
    t1.start()
    #print('主線程',current_process().pid)#主線程 19664
    print('主進程',os.getpid())#主進程 7760


#區別4,瞅一眼pid
from threading import Thread
import os

def task4():
    print('子線程PID:%s '%(os.getpid()))#子線程PID:12248

if __name__ == '__main__':
    t1 = Thread(target=task4,)
    t1.start()
    print('主線程',os.getpid())#主線程 12248

#注意:其實沒有子線程PID,線程都是相同級別的,你們地位相等,爲了便於理解,纔在此處稱之爲:子線程;
複製代碼

24-Thread對象的其餘屬性或方法

一、方法介紹;

複製代碼
Thread實例對象的方法
  # isAlive(): 返回線程是否活動的。
  # getName(): 返回線程名。
  # setName(): 設置線程名。

threading模塊提供的一些方法:
  # threading.currentThread(): 返回當前的線程變量。
  # threading.enumerate(): 返回一個包含正在運行的線程的list。正在運行指線程啓動後、結束前,不包括啓動前和終止後的線程。
  # threading.activeCount(): 返回正在運行的線程數量,與len(threading.enumerate())有相同的結果。
複製代碼

二、方法驗證;

複製代碼
#!/usr/bin/env python3
# -*- coding:utf-8 -*-
# __Author__:TQTL911
# Version:python3.6.6
# Time:7/20/2018 5:12 PM
from threading import Thread,current_thread,active_count,enumerate
import time

def task():
    print('%s is runing'%current_thread().getName())
    time.sleep(2)
    print('%s is done'%current_thread().getName())

if __name__ == '__main__':
    t = Thread(target= task,name='子線程1')
    t.start()
    #t.setName('兒子線程1')
    #current_thread().setName('主線程')
    #print(t.is_alive())#返回值爲True或者False
    #print(t.isAlive())#返回值爲True或者False
    #print(t.getName())
    #print('主線程',current_thread().getName())
    #t.join()
    print(active_count())
    print(enumerate())#[<_MainThread(MainThread, started 10096)>, <Thread(子線程1, started 14432)>]
複製代碼

25-守護線程

一、守護線程初識;

不管是進程仍是線程,都遵循:守護xxx會等待主xxx運行完畢後被銷燬;

1)須要強調的是:運行完畢並不是終止運行;

一、對主進程來講,運行完畢指的是主進程代碼運行完畢;

二、對主線程來講,運行完畢指的是主線程所在的進程內全部非守護線程通通運行完畢,主線程纔算運行完畢;

2)詳細解釋;

一、主進程在其代碼結束後就已經算運行完畢了(守護進程在此時就被回收),而後主進程會一直等非守護的子進程都運行完畢後回收子進程的資源(不然會產生殭屍進程),纔會結束;

二、主線程在其餘非守護線程運行完畢後纔算運行完畢(守護線程在此時就被回收)。由於主線程的結束意味着進程的結束,進程總體的資源都將被回收,而進程必須保證非守護線程都運行完畢後才能結束;

3)驗證一下吧!

複製代碼
#!/usr/bin/env python3
# -*- coding:utf-8 -*-
# __Author__:TQTL911
# Version:python3.6.6
# Time:7/20/2018 5:27 PM
""
"""
 25-守護線程
"""
from threading import Thread
import time

def sayhi(name):
    time.sleep(1)
    print('%s say hello'%name)

if __name__ == '__main__':
    t = Thread(target= sayhi,args= ('cuixiaozhao',))
    #t.setDaemon(True)#必須在t.start()以前設置;
    t.daemon = True #做用同上;
    t.start()

    print('主線程')
    #print(t.isAlive())
    print(t.is_alive())#做用同上;


from threading import Thread
import time

def foo():
    print(123)
    time.sleep(1)
    print('end123')

def bar():
    print(456)
    time.sleep(3)
    print('end456')
"""
123
456
--------main--------
end123
end456
"""

if __name__ == '__main__':
    t1 = Thread(target= foo)
    t2 = Thread(target=bar)

    t1.daemon = True
    t1.start()
    t2.start()
    print('main'.center(20,'-'))
複製代碼

26-互斥鎖

一、互斥鎖概念再次初識;

 

複製代碼
#!/usr/bin/env python3
# -*- coding:utf-8 -*-
# __Author__:TQTL911
# Version:python3.6.6
# Time:7/20/2018 5:47 PM
""
"""
 26-互斥鎖-由並行改成串行,犧牲了性能,保證了數據安全;
 
"""
#mutex
from threading import Thread,Lock
import time

n = 100
def task():
    global n
    mutex.acquire()
    temp = n
    time.sleep(0.1)
    n = temp -1
    mutex.release()

if __name__ == '__main__':
    mutex = Lock()#局部加鎖
    t_l = []
    for i in range(100):
        t = Thread(target= task,)
        t_l.append(t)
        t.start()

    for t in t_l:
        t.join()
    print('主',n)
"""
小結:
一、犧牲了效率;
二、保證了數據安全;
"""
複製代碼

 

27-GIL的基本概念

28-GIL與互斥鎖的區別

29-GIL與多線程

30-死鎖與遞歸鎖

31-信號量

32-Event事件

33-定時器

34-線程queue

35-多線程實現併發的套接字通訊

36-進程池線程池

37-異步調用與回調機制

38-進程池線程池小練習

39-協程介紹

40-greenlet模塊

41-greenlet模塊

42-gevent模塊

43-gevent異步提交任務

44-基於gevent模塊實現併發的套接字通訊

45-IO模型介紹

46-阻塞IO模型

47-非阻塞IO模型

48-多路複用IO模型

49-異步IO模型

相關文章
相關標籤/搜索