学习D17-D27

##1.0 进程

1.1 程序与 进程

进程:是系统进行资源分配的基本单位

进程:动态的概念,运行起来的程序,包括程序,以及运行程序所需要的计算机资源等

程序:静态的概念,我们写的.py .go等,都是程序

1.2 多进程的概念

import multiprocessing
import os

num = 0

def add_num1():
    global num
    for i in range(1000000):

        num += 1
            #释放锁

    print('子进程1已经完成,此时得到的num','当前子进程编号',num,os.getpid(),'当前子进程父进程编号(主进程)',os.getppid())




def add_num2():
    global num
    for i in range(1000000):
        num += 1


    print('子进程2已经完成,此时得到的num',num)

if __name__ == '__main__':
    print('主进程编号',os.getpid())
    process1 = multiprocessing.Process(target=add_num1)
    process2 = multiprocessing.Process(target=add_num2)

    process1.start()
    process2.start()
    
注意点:
1、os.getpid获取执行当前任务的进程编号
2、os.getppid获取的是当前进程的父进程编号
3、多进程之间不共享全局变量,多个进程之间是独立的

###1.3 传参

def add_num1(count,name):
    global num
    for i in range(count):

        num += 1
            #释放锁

    print('子进程1已经完成,此时得到的num',num,'当前子进程编号',os.getpid(),'当前子进程父进程编号(主进程)',os.getppid())
    
process1 = multiprocessing.Process(target=add_num1,args=(1000,),kwargs={'name':'小王'})

1.4 队列

try:
    for i in range(4):
        que.put('123',True,1)
except Exception as e:
    print('消息队列已经满了,','当前数据有%s个'%que.qsize())
    
注意点:
1、put在默认情况下,如果队列已经满了,一直阻塞,等待队列出现空余位置
2、如果设置了timeout,那么等待过后,会报错
3、block设置为False,直接报错



if not que.full():
    for i in range(que.qsize()):
        que.put_nowait('123')
from multiprocessing import Queue,Process

que = Queue(3)
# que.put('123')
# que.put('123')
# # que.put('123',block=True,timeout=10)
# # que.put('123')
# # que.put('123')

# que.get()


# try:
#     for i in range(4):
#         que.put('123',True,1)
# except Exception as e:
#     print('消息队列已经满了,','当前数据有%s个'%que.qsize())

# if not que.full():
#     for i in range(que.qsize()):
#         que.put_nowait('123')


def readData(que):
    if not que.empty():

        data = que.get_nowait()
        print(data)

def writeData(que):
    if not que.full():
        try:
            que.put_nowait('123')
        except :
            print('出错')

if __name__ == '__main__':
    q = Queue()
    p1 = Process(target=writeData,args=(q,))
    p2 = Process(target=readData,args=(q,))

    p1.start()

    import time
    time.sleep(10)

    p2.start()
from multiprocessing import Pool
import os


def info():
    print('执行该函数的进程编号',os.getpid())
    import time
    time.sleep(1)
    print('这是一个函数')




if __name__ == '__main__':
    po = Pool(3)
    print(po)
    for i in range(20):
        po.apply_async(info)
    print('############开始#############')
    #将进程池关闭,不再接收新的请求
    po.close()
    #等待进程池中的进程全部结束
    po.join()
    print('###############结束########3')

1.5 进程与线程的区别

1、线程是进程中的一个实例,真正干活的人是线程,

2、进程只是资源分配的基本单位,线程是调度的基本单位

3、没有进程就没有线程这个概念

4、不同的进程一起运行,其中一个进程死了,其他进程不受影响

5、在同一个进程内,如果线程消亡,那这个进程受影响,所以从健壮性来讲,进程比线程更加稳定

6、线程共享全局变量,进程创建子进程相当于粘贴复制,此时相当于创建了一份副本,不共享全局变量

2、线程

2.1 定义

默认情况下,程序启动只有一个线程,这个线程就是主线程,线程是CPU调度的基本单位

2.2 统计线程的个数

import threading
print('hello')
print('#######统计当前线程运行的个数######')
print(threading.active_count())

展示:
hello
#######统计当前线程运行的个数######
1

注意点:
在没有添加进程的情况下,我们依旧可以找到一个线程,这个线程是主线程

2.3 创建多线程

def __init__(self, group=None, target=None, name=None,
             args=(), kwargs=None, *, daemon=None):
             
import threading
import time
# print('hello')
# print('#######统计当前线程运行的个数######')
# print(threading.active_count())


def calm():
    for i in range(10):
        time.sleep(0.1)
        print('第',i,'次冷静一下')

def smoke():
    for j in range(10):
        time.sleep(0.1)
        print('第',j,'支烟')



if __name__ == '__main__':
    # smoke()
    # calm()
    print('当前进程的线程个数',threading.active_count())
    #创建多线程
    thread_smoke = threading.Thread(target=smoke)
    print('当前进程的线程个数', threading.active_count())

    thread_calm = threading.Thread(target=calm)
    #调用
    thread_smoke.start()
    thread_calm.start()
    print('当前进程的线程个数', threading.active_count())


展示:
当前进程的线程个数 1
当前进程的线程个数 1
当前进程的线程个数 3
第 0 支烟
第 0 次冷静一下
第 1 支烟
第 1 次冷静一下
第 2 支烟
第 2 次冷静一下
第 3 次冷静一下
第 3 支烟
第 4 支烟
第 4 次冷静一下
第 5 次冷静一下
第 5 支烟
第 6 次冷静一下
第 6 支烟
第 7 支烟
第 7 次冷静一下
第 8 支烟
第 8 次冷静一下
第 9 次冷静一下
第 9 支烟

2.4 多线程的注意点

1、线程的执行是无序的,因为是CPU进行调度的

2、守护主线程,主线程退出,那子线程直接销毁

import threading
import time
# print('hello')
# print('#######统计当前线程运行的个数######')
# print(threading.active_count())


def calm():
    for i in range(10):
        time.sleep(0.1)
        print('第',i,'次冷静一下')

def smoke():
    for j in range(10):
        time.sleep(0.1)
        print('第',j,'支烟')



if __name__ == '__main__':
    # smoke()
    # calm()
    print('当前进程的线程个数',threading.active_count())
    #创建多线程
    thread_smoke = threading.Thread(target=smoke)
    print('当前进程的线程个数', threading.active_count())

    thread_calm = threading.Thread(target=calm)
    #守护主线程
    thread_smoke.setDaemon(True)
    thread_calm.setDaemon(True)


    #调用
    thread_smoke.start()
    thread_calm.start()
    # time.sleep(0.1)

    # thread_calm.join()
    # thread_smoke.join()
    print('当前进程的线程个数', threading.active_count())
    print('主线程已经结束')
    exit()
    
    展示:
    当前进程的线程个数 1
    当前进程的线程个数 1
    当前进程的线程个数 3
    主线程已经结束

3、守护子线程,只有当子线程全部结束之后,主线程才结束

def calm():
    for i in range(10):
        time.sleep(0.1)
        print('第',i,'次冷静一下')

def smoke():
    for j in range(10):
        time.sleep(0.1)
        print('第',j,'支烟')



if __name__ == '__main__':
    # smoke()
    # calm()
    print('当前进程的线程个数',threading.active_count())
    #创建多线程
    thread_smoke = threading.Thread(target=smoke)
    print('当前进程的线程个数', threading.active_count())
    thread_calm = threading.Thread(target=calm)

    #调用
    thread_smoke.start()
    thread_calm.start()
    # time.sleep(0.1)
    
    thread_calm.join()
    thread_smoke.join()
    print('当前进程的线程个数', threading.active_count())
    print('主线程已经结束')
    exit()
    
    展示:
    当前进程的线程个数 1
当前进程的线程个数 1
第 0 支烟
第 0 次冷静一下
第 1 支烟
第 1 次冷静一下
第 2 支烟
第 2 次冷静一下
第 3 支烟
第 3 次冷静一下
第 4 次冷静一下
第 4 支烟
第 5 次冷静一下
第 5 支烟
第 6 支烟
第 6 次冷静一下
第 7 支烟
第 7 次冷静一下
第 8 次冷静一下
第 8 支烟
第 9 支烟
第 9 次冷静一下
当前进程的线程个数 1
主线程已经结束

4、子线程之间共享全局变量,就会造成资源的争抢问题

2.5 互斥锁的问题

问题:

因为线程之间共享全局变量,所以,在多线程几乎在同时运行的时候,几乎同时修改同一个全局变量的时候,就要进行控制

此时,需要互斥锁

当某个线程需要修改资源的时候,先将资源进行锁定,其他线程不能修改该线程

当线程修改完成之后,并且释放完互斥锁之后,其他的线程才可以使用

互斥锁保证在当前只有一个线程可以使用修改同一个资源

import threading


num = 0

def add_num1():
    global num
    for i in range(1000000):
        #上锁
        lock_flag = lock_obj.acquire(True)
        if lock_flag:
            num += 1
            #释放锁
            lock_obj.release()
    print('子线程1已经完成,此时得到的num',num)



def add_num2():
    global num
    for i in range(1000000):
        # 上锁
        lock_flag = lock_obj.acquire(True)
        if lock_flag:
            num += 1
            # 释放锁
            lock_obj.release()
    print('子线程2已经完成,此时得到的num',num)


if __name__ == '__main__':

    lock_obj = threading.Lock()
    thread1 = threading.Thread(target=add_num1)
    thread2 = threading.Thread(target=add_num2)

    thread1.start()
    # import time
    # time.sleep(5)
    thread2.start()
    
   注意点:
   1、确保了关键代码只能由一个线程从头到尾完整的执行完成
   2、阻止了多线程的并发操作,包含锁的代码其实还是单线程执行,效率下降了;
   	  如果当前的程序出现了多个锁,可能会出现死锁的问题
   

2.6 死锁的问题

避免死锁的方法:

1、超时释放

2、设计的时候,避免死锁

2.7 线程安全

from queue import Queue

from Queue import Queue #python2

3、协程

微线程,由程序员调度,线程:CPU调度的

3.1 实现协程

import time

def work1():
    for i in range(5):
        print('work1',i)
        yield
        print('11111111111')
        time.sleep(1)

def work2():
    for i in range(5):
        print('work2',i)
        yield
        time.sleep(1)

w1 = work1()
w2 = work2()
# while True:
next(w1)
next(w2)
next(w2)
next(w1)
注意点:
利用的是yeild这个属性,遇到yield会暂停执行,完成任务的切换

import time
from greenlet import greenlet

def work1():
    for i in range(5):
        print('work1',i)
        g2.switch()
        print('######已经切换了g2#####')
        time.sleep(1)


def work2():
    for i in range(5):
        print('work2',i)
        g1.switch()
        time.sleep(1)



g1 = greenlet(work1)
print(g1)

g2 = greenlet(work2)
print(g2)

g2.switch()

注意点:
需要手动取切换协程
但是已经封装了协程了,比原始版本进化一点
import gevent
import time

from gevent import monkey

monkey.patch_all()


def work1():
    for i in range(5):
        print('work1',i,gevent.getcurrent())
        time.sleep(1)
        # gevent.sleep(1)

def work2():
    for i in range(5):
        print('work2',i,gevent.getcurrent())
        time.sleep(1)
        # gevent.sleep(1)

# g1 = gevent.spawn(work1)
# g2 = gevent.spawn(work2)
#
#
# g1.join()
# g2.join()

gevent.joinall([
    gevent.spawn(work1),
    gevent.spawn(work2)
])

4、网络通信概述

###4.1 网络的定义

网络:辅助双方或者多方能够连接在一起的工具,用来进行数据交换

协议:约定俗成的规定

tcp/ip协议:计算机遵守的网络通信协议

###4.2 端口

个数:2的16次方(65536)(0-65535)

端口是数据发出或接收的入口

端口的目的:通过端口号找到对应的进程,完成数据的通信

著名端口0-1023

动态端口:2014-65535

###4.3 IP

IP地址:标注在网络中标记一台计算机的一串数字,例如192.168.24.1 ,在本地局域网上是唯一的

注意:127.0.0.1 代表的是本机的IP地址

5、socket

简称:套接字,是进程之间进行通信的一种方式

实现了不同主机之间的进程通信,我们现在使用的大多数的服务:qq、浏览网页等基于socket来完成通信的

5.1 python 中socket

def __init__(self, family=AF_INET, type=SOCK_STREAM, proto=0, fileno=None):
    # For user code address family and type values are IntEnum members, but
    # for the underlying _socket.socket they're just integers. The
    # constructor of _socket.socket converts the given argument to an
    # integer automatically.
    _socket.socket.__init__(self, family, type, proto, fileno)
    self._io_refs = 0
    self._closed = False
    
    注意点:
    1、family=AF_INET,表示的用于Internet之间的通信
    2、type=SOCK_STREAM,是指流式套接字,用于tcp协议
    3、type=SOCK_DGRAM数据报套接字,用于udp协议

5.2 udp发送数据

def sendto(self, data, flags=None, *args, **kwargs): # real signature unknown; NOTE: unreliably restored from __doc__ 
    """
    sendto(data[, flags], address) -> count
    
    Like send(data, flags) but allows specifying the destination address.
    For IP sockets, the address is a pair (hostaddr, port).
    """
    pass
    
    注意点:
    1、sendto()这个方法发送的数据,是bytes类型,普通字符串用encode()转化成bytes
    2、address 用来指定对方的IP和端口,打包成元组
    

5.3 udp接收数据

def recvfrom(self, buffersize, flags=None): # real signature unknown; restored from __doc__
    """
    recvfrom(buffersize[, flags]) -> (data, address info)
    
    Like recv(buffersize, flags) but also return the sender's address info.
    """
    pass
    
    注意点:
    1、返回值,返回(数据,发送该条信息的地址)
    2、数据接收过来是bytes类型的,需要用decode()转换成普通的字符串
   

5.4 bind端口

def bind(self, address): # real signature unknown; restored from __doc__
    """
    bind(address)
    
    Bind the socket to a local address.  For IP sockets, the address is a
    pair (host, port); the host must refer to the local host. For raw packet
    sockets the address is a tuple (ifname, proto [,pkttype [,hatype]])
    """
    pass
    
    socket_udp_client.bind(('',9000))
    ''代表本机的任何一个地址都可以

6、 UDP协议

5.1 特点

面向无连接的通讯协议

5.2 udp服务端的通信流程

1、创建一个socket套接字

2、绑定端口

3、接收服务端传递过来的请求

4、处理请求,发送数据给客户端

5、关闭套接字

5.3 UDP 客户端通信流程

1、创建套接字

2、发送请求(数据)

3、接收数据

4、关闭套接字

7、tcp协议

7.1 基于tcp协议的客户端

1、创建套接字

2、与服务端创建连接

3、发送数据

4、接收数据

5、关闭套接字

###7.2 基于tcp协议的服务端

1、创建socket套接字

2、bind绑定服务端的IP和端口

3、listen监听,使服务端的套接字从主动状态变成被动状态,等待连接

4、accept等待客户端的连接,运行之后是阻塞状态,直到有连接

5、收发数据

6、关闭套接字

8 、请求方式:

8.1 get

最常用的请求方式,用来向后端请求资源的,会将部分的参数拼接在URL地址上

http://tieba.baidu.com/f?kw=美食&ie=utf-8&pn=15000

?后面拼接的都是需要向后端传递的数据参数

8.2 post

也是常用的请求方式,但是它会将需要传递的数据,放置在post的请求体中,传递比get更安全,所以post请求中的数据可能是敏感数据:用户名和密码等

向指定资源提交数据进行处理请求(提交表单、上传文件),又可能导致新的资源的建立或原有资源的修改

8.3 delete

请求服务器删除request-URL所标示的资源*(请求服务器删除页面)

8.4 put

向指定资源位置上上传其最新内容(从客户端向服务器传送的数据取代指定文档的内容)

8.5 opions

返回服务器针对特定资源所支持的HTML请求方法 或web服务器发送*测试服务器功能(允许客户端查看服务器性能

9 请求报文的解析

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值