python并发编程笔记

目录

1 进程 

1. windows 下进程要在main下创建 

2. 循环创建子进程,可以先start,加入列表中,再执行join,下面这执行完一共3秒。

3. 进程间数据相互隔离

4. 计算机会给每一个进程分配一个PID号。

5.杀死进程

6. 僵尸进程和孤儿进程

7.守护进程

8. 互斥锁

9. 队列

10.进程间通信 IPC

11.生产者消费者模型

2 线程

1.创建线程

2. TCP服务端并发效果

3.线程补充方法

4.守护线程

5.互斥锁

6.GIL全局解释系统

7.Python多线程是否有用(多核)

8. 死锁(了解)

9.递归锁(了解)

10.信号量(了解)

11.Event事件(了解)

12.线程q(了解)

13.进程池与线程池

3 协程

1.gevent模块

4 IO模型

1.阻塞IO

2.非阻塞IO

3.IO多路复用

4. 异步IO



1 进程 

1. windows 下进程要在main下创建 

from multiprocessing import Process
import time


def task(name, n):
    print('%s开始运行' % name)
    time.sleep(n)
    print('%s结束运行' % name)


if __name__ == '__main__':
    p1 = Process(target=task, args=('wang', 3))    # 用元组传参数,一个参数后也要加逗号
    p2 = Process(target=task, args=('li', 4))
    p1.start()  # 激活进程
    p2.start()
    p1.join()  # 让主进程在进程执行完后再执行
    p2.join()
    print("主进程")

2. 循环创建子进程,可以先start,加入列表中,再执行join,下面这执行完一共3秒。

    start_time = time.time()
    p_list = []
    for i in range(1,4):
        p = Process(target=task, args=('子进程%s' %i, i))
        p.start()
        p_list.append(p)
    for p in p_list:
        p.join()
    print('主进程', time.time() - start_time)

3. 进程间数据相互隔离

n = 100


def test():
    global n
    n = 600
    print('子进程', n)
if __name__ == '__main__':
   p = Process(target=test)
   p.start()
   p.join()
   print('主进程', n)

输出:子进程600

           主进程100

在子进程中复制了一个n修改成了600,但不影响主进程的数据。

4. 计算机会给每一个进程分配一个PID号。

        windows  cmd进入命令行输入tasklist即可查看。

        tasklist | findstr PID 查看具体进程

from multiprocessing import Process, current_process
import time
import os


def task2():
    print('%s is running' % current_process().pid)  # 查看当前进程PID(方法一)
    print('%s is running' % os.getpid())  # 查看当前进程PID(方法二)
    print('%s is running' % os.getppid())  # 查看当前进程的主进程PID(方法二)

    time.sleep(30)

5.杀死进程

让操作系统去结束进程,需要一点时间,而代码的运行速度很快,所以要稍等一点时间。

p.terminate()  # 杀死当前进程
time,sleep(0.1)
print(p.is_alive())  # 判断当前进程是否存活

6. 僵尸进程和孤儿进程

僵尸进程是杀死但没有死透, 没有立刻释放进程号, 之后会被父进程回收。

孤儿进程是子进程存活但父进程意外死亡,操作系统会管理和回收孤儿进程。

7.守护进程

主进程结束,守护进程也会结束。

这句话设置一定要放到p.start()上面,否则会报错。

p.daemon = True  # 将p设置成守护进程

8. 互斥锁

多个进程操作同一份数据的时候(如十个人买一张票),会造成数据错乱。

解决办法就是加锁,将并发改成串行,牺牲效率保证数据安全。

只在数据互相争抢的时候加锁

9. 队列

from multiprocessing import Process, Queue

q = Queue(5)  # 括号内参数表示最大可以存放数据
# 存数据 存满了继续存会阻塞
q.put(111)
print(q.full()) # 队列是否满
print(q.empty()) # 队列是否空
# 取数据 取完了再取会阻塞
v = q.get()
v1 = q.get_nowait()  # 没有数据会报错
v3 = q.get(timeout=3)  # 没有数据等三秒再报错

full, empty, get_nowait 在多进程时是不精确的。 队列 = 管道 + 锁

10.进程间通信 IPC

一个进程向队列中放数据,一个从队列中取数据。

def producer(q):
    q.put('我是1号')
    print('hello')


if __name__ == '__main__':
    q = Queue()
    p = Process(target=producer, args=(q,))
    p.start()
    print(q.get())  # 不用join ,get没拿到东西会继续等待

11.生产者消费者模型

两者间需要媒介沟通

要将消费者设置成主进程的守护进程p.deamon = True

不然消费者会一直get数据而阻塞

from multiprocessing import Process, Queue, JoinableQueue

if __name__ == '__main__':
    q = JoinableQueue()  # 每添加一个数据,计数器会加一
    q.task_done()  # 告诉队列已经取出来一个数据并处理完毕了

    q.join() # 等队列所有数据被取完了再往后执行

12 Timer

延时一定时间后开始执行某函数

from threading import Timer
from threading import Thread



def func(x, y):
    print(x + y)


if __name__ == '__main__':
    t = Timer(3, func, args=(3,4))
    t.start()

 

2 线程

进程是资源单位,线程是执行单位。 进程是工厂,线程是流水线。

每一个进程肯定自带一个线程。

进程开辟一块独立空间,线程执行时的资源招进程索要。

1.创建线程

和创建进程类似,但创建线程不需要复制代码,所以不用再main里创建,并且start后会快速开始

from threading import Thread
import time

def task(name):
    print('%s is running' % name)
    time.sleep(3)
    print('%s is over' % name)


t = Thread(target=task, args=('www',))
t.start()
print('主')

2. TCP服务端并发效果

windows 不支持多进程,端口号会冲突,用多线程。

import socket
from threading import Thread
from multiprocessing import Process

'''
服务端要求:
    1.固定的IP和PORT
    2.24小时不间断服务
    3.能够支持并发
'''

sever = socket.socket()  # 默认是TCP协议
sever.bind(('127.0.0.1', 8080))
sever.listen(5)


def task(conn):
    while True:
        try:
            data = conn.recv(1024)
            if len(data) == 0: break
            print(data.decode('utf-8'))
            conn.send(data.upper())
        except ConnectionError as e:
            print(e)
            break


while True:
    conn, addr = sever.accept()
    t = Thread(target=task, args=(conn,))
    t.start()

3.线程补充方法

1        join()使主线程程等待线程运行完毕再继续

2        在线程中声明global 变量,线程间可以相互通信。

3        os.getpid() 主线程和子线程下PID号是一样的

from threading import Thread, active_count, current_thread
import time


def task(name):
    print('hello', current_thread().name)
    time.sleep(3)


if __name__ == '__main__':
    t = Thread(target=task, args=('www',))
    t.start()
    print('主', active_count())  # 统计当前活跃的线程数
    print('主', current_thread().name)  # 获取线程名字

4.守护线程

主线程运行结束后不会立刻结束,要等其他非守护线程结束才会结束

在t.start()前加

t.daemon = True

  注:如果非守护线程结束的慢而守护线程结束的快,那么全都会执行完毕。

5.互斥锁

from threading import Thread, Lock
import time

money = 100


def task():
    global money
    mutex.acquire()  #  争抢锁
    tmp = money
    time.sleep(0.1)
    money = tmp - 1
    mutex.release()  #  释放锁


if __name__ == '__main__':
    mutex = Lock()
    t_list = []
    for i in range(100):
        t = Thread(target=task)
        t.start()
        t_list.append(t)
    for t in t_list:
        t.join()
    print(money)

6.GIL全局解释系统

“GIL不是python特点而是Cpython的特点

GIL是保证解释器级别的数据安全,是解释性语言的通病

GIL会导致同一个进程下的多个线程无法同时执行,即无法利用多核优势

针对不同的数据还是要加不同的锁”

在CPython解释器中GIL是一把互斥锁,用来阻止同一个进程的多线程的同时执行

因为CPython中的内存管理不是线程安全的

内存管理(垃圾回收机制)

        1.应用计数

        2.标记清除

        3.分代回收

7.Python多线程是否有用(多核)

计算密集型

多进程优于多线程

IO密集型

多线程节省资源,优于多进程

多进程和多线程都有各自优势,实际使用中通常多进程下开设多线程。

8. 死锁(了解)

两把锁在对方手中,都需要枪对方的锁,导致程序阻塞。

这个代码会产生死锁,线程1执行func2时抢到了B锁,别的线程执行func1时抢到了A锁,双方都要对方的锁,导致程序卡住。 

from threading import Thread, Lock, RLock

mutexA = Lock()
mutexB = Lock()


class MyThead(Thread):
    def run(self):
        self.func1()
        self.func2()

    def func1(self):
        mutexA.acquire()
        print('%s 抢到A锁' % self.name)
        mutexB.acquire()
        print('%s 抢到B锁' % self.name)
        mutexB.release()
        mutexA.release()

    def func2(self):
        mutexB.acquire()
        print('%s 抢到B锁' % self.name)
        time.sleep(2)
        mutexA.acquire()
        print('%s 抢到A锁' % self.name)
        mutexA.release()
        mutexB.release()


if __name__ == '__main__':
    for i in range(10):
        t = MyThead()
        t.start()

9.递归锁(了解)

可以连续的acquire 和release

但只能被第一个抢到的执行操作内部有一个计数器,只有当计数器清零,才能被其他使用

把上个代码稍微改动,即可避免死锁。

from threading import Thread, Lock, RLock

mutexA = mutexB = RLock()

10.信号量(了解)

互斥锁相当于一堆人抢一个房间,信号量相当于一堆人抢好几个房间。 

from threading import Thread, Lock, RLock, Semaphore
import random
import time

sm = Semaphore(5)

def task(name):
    sm.acquire()
    print('%s 正在通信' % name)
    time.sleep(random.randint(1,5))
    #time.sleep(3)
    sm.release()


if __name__ == '__main__':
    for i in range(20):
        t = Thread(target=task, args=('第%d个人'%i,))
        t.start()

11.Event事件(了解)

一些进程/线程要等待另外的一些进程/线程运行完毕后才能运行。

import time
from threading import Thread, Event

event = Event()


def light():
    print('红灯亮着')
    time.sleep(3)
    print('绿灯亮了')
    event.set()


def car(name):
    print('%s 等红灯中' % name)
    event.wait()
    print('%s 车开走了' % name)



if __name__ == '__main__':
    t = Thread(target=light)
    t.start()

    for i in range(20):
        t = Thread(target=car, args=('%s' % i,))
        t.start()

12.线程q(了解)

队列 = 管道 + 锁

先进先出q :  在进程中介绍过

 后进先出q:q = queue.lifoqueue(3) put, get方法

优先级q: 可以给放入队列的数据设置优先级

        q = queue.PriorityQueue(3)

        q.put((10, '111'))  第一个参数是优先级,优先级可以是负数,且数字越小,优先级越高

13.进程池与线程池

因为硬件资源的限制,不能无限制的开设进程和线程。
要保证计算机能正常工作的情况下最大限度利用它。

池是用来保证计算机安全的情况下最大限度地利用下计算机,降低了程序的运行效率但是保证了计算机硬件的安全,从而让程序能正常工作。

线程池

from concurrent.futures import ThreadPoolExecutor, ProcessPoolExecutor
import time

pool = ThreadPoolExecutor(5)  # 池子里固定5个,不会重复创建和销毁


# 不传参数默认是CPU核数的五倍线程数
# 只需要把任务提交到池子里即可


def task(n):
    print(n)
    time.sleep(1)
    return n ** n


pool.submit(task, 1)  # 提交任务  异步提交
print('主')
t_list = []
for i in range(20):
    res = pool.submit(task, i)
    # print(res.result())  # 调用了result方法,变成了串行,回调函数拿到了异步提交的结果 join
    t_list.append(res)

pool.shutdown() # 关闭线程池, 等待线程池所有任务运行完毕
for t in t_list:
    print('>>>', t.result())  # 结果时有序的

进程池

异步回调

from concurrent.futures import ThreadPoolExecutor, ProcessPoolExecutor
import time

pool = ProcessPoolExecutor()  # 进程数固定,不会重复创建和销毁


# 不传默认是CPU的核数


def task(n):
    print(n)
    time.sleep(1)
    return n ** n


def call_back(n):
    print('call_back>>>', n.result())


#  回调机制  是绑定在进程身上的定时炸弹,一旦任务有结果立刻触发爆炸

if __name__ == '__main__':
    t_list = []
    for i in range(20):
        res = pool.submit(task, i).add_done_callback(call_back)

总结

 from concurrent.futures import ThreadPoolExecutor, ProcessPoolExecutor

pool = ProcessPoolExecutor()

 res = pool.submit(task, i).add_done_callback(call_back)

3 协程

协程是想象出来的,根本不存在。指的是单线程下,实现并发。

程序员自己在代码层面上检测所有IO操作,一旦遇到IO,在代码级别完成切换,从而使CPU觉得程序一直在运行,提高了程序运行效率。

多道技术:

        切换+保存状态  yield

        CPU切换两种状态:

                1.程序遇到IO

                2.程序长时间占用。

TCP服务端 accept recv

切换不一定提升效率(IO),也可能降低效率(计算)

1.gevent模块

检测IO操作,自动切换。耗时和最长时间有关。

from gevent import monkey
monkey.patch_all()
import time
from gevent import spawn


def heng():
    print("hengheng")
    time.sleep(2)


def ha():
    print("haha")
    time.sleep(3)


start_time = time.time()
g1 = spawn(heng)
g2 = spawn(ha)
g1.join()
g2.join()
print(time.time() - start_time)

单线程也能玩出并发的效果,可以在多进程开设多线程,多线程下开多协程,从而使程序执行效率提高。

4 IO模型

recv , accept,  recvfrom使IO状态。

1.阻塞IO

开设了多进程多线程,依然没有解决IO问题。该等的地方还是在等,只不过多个人等待互不干扰。

2.非阻塞IO

提交之后无论是否有数据,都会立刻得到一个结果, 不会阻塞住。

将所有的阻塞操作都变成了非阻塞。

import socket
import time

server = socket.socket()
server.bind(('127.0.0.1', 8080))
server.listen(5)
server.setblocking(False)  # 将所有的网络阻塞变成非阻塞

r_list = []
del_list = []
while True:
    try:
        conn, addr = server.accept()
        r_list.append(conn)
    except BlockingIOError:
        # time.sleep(0.1)
        # print('列表的长度', len(r_list))
        # print('做其他事')
        for conn in r_list:
            try:
                data = conn.recv(1024)  # 没有消息会报错
                if len(data) == 0:  # 客户端断开链接
                    conn.close()
                    del_list.append(conn)
                    continue
                conn.send(data.upper())
            except BlockingIOError:
                continue
            except ConnectionError:
                conn.close()
                del_list.append(conn)
        for conn in del_list:  # 清除无用的链接
            r_list.remove(conn)
        del_list.clear()
import socket

client = socket.socket()
client.connect(('127.0.0.1', 8080))

while True:
    msg = input('>>>')
    client.send(msg.encode('utf-8'))
    data = client.recv(1024)
    print(data)

虽然非阻塞IO很厉害,但是该模型会长时间占用着CPU空转,浪费在了while True循环上

实际应用中也不会开率使用非阻塞IO模型

3.IO多路复用

操作系统提供监管机制,监管socket 和conn对象,只要被触发了,立刻返回可执行对象。

监管一个对象时,效率低于阻塞IO,但可以监管多个对象。

selestors模块

4. 异步IO

是所有模型中效率最高,使用最广泛的模型。

模块 asyncio模块

框架 sanic tronado twisted

http://www.cnblogs.com/linhaifeng/articles/5937962.html

5.网络并发编程知识点

1.软件开发架构 CS BS

2.互联网协议 osi七层/五层协议 每层作用,以太网协议,广播风暴,IP协议

3.TCP UDP

4. 三次握手四次挥手(重点)

5.socket简介

6.TCP粘包问题,定制固定长度的报头

7.UDP协议

8.socketsever模块

1.操作系统发展

2.多道技术

3.进程理论

4.开启进程的两种方式

5.互斥锁

6生产者消费者模型

7.线程理论

8.开启线程的两种方式

9.GIL全局解释器

10.进程/线程使用

11.协程概念

12.IO模型了解

            

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

borui星空

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值