回顾一下py基础(socket,多线程/多进程/进程池,GIL全局解释器锁)

socket(套接字):

server

import socket
sk = socket.socket(socket.AF_INET, socket.SOCK_STREAM, 0)
address = ('127.0.0.1',8000)
sk.bind(address)  # 为socket绑定ip地址和端口号
sk.listen()  # 监听,队列大小为5
while(True):
    print('等待客户端链接' + '.' * 20)
    connect, client = sk.accept()
    print(str(client) + '上线')
    while True:
        try:
            inp = input('>>>请输入:')
            connect.send(bytes(inp, 'utf8'))
            data = connect.recv(1024)
            if not data: break
            print(str(data, 'utf8'))
        except ConnectionResetError:
            print('-' * 10 + '对方离线' + '-' * 10)
            break
sk.close()

client:

import socket

sk=socket.socket()

try:
    sk.connect(('127.0.0.1',8000))
except ConnectionRefusedError:
    print('-'*10+'服务器未上线,或者不存在'+'-'*10)
    exit()

while True:
    try:
        data = sk.recv(1024)
    except ConnectionResetError:
        print('-'*10+'服务器歇菜了'+'-'*10)
        break
    print(str(data, 'utf8'))
    inp = input('>>>请输入:')
    if inp =='exit':
        break
    sk.send(bytes(inp, 'utf8'))
sk.close()

最简单的创建子进程:

import os

print('当前进程:%s 启动中 ....' % os.getpid())
pid = os.fork() #运行这一步后,操作系统会把当前进程的数据复制一遍,然后程序就分两个进程继续运行后面的代码。
if pid == 0:
    print('子进程:%s,父进程是:%s' % (os.getpid(), os.getppid()))
else:
    print('进程:%s 创建了子进程:%s' % (os.getpid(),pid ))

python的多线程:

进程与线程均为操作系统资源管理方式。
线程是进程的一个实体,是CPU调度和分派的基本单位,它是比进程更小的能独立运行的基本单位.线程自己基本上不拥有系统资源,
只拥有一点在运行中必不可少的资源(如程序计数器,一组寄存器和栈),但是它可与同属一个进程的其他的线程共享进程所拥有的全部资源。
线程中全局、静态数据是共享的,而进程之间数据是独立的。
同一个进程之间的线程可以直接通信,但是进程之间通信需要找其他的方法。
线程启动/切换速度快,进程启动/切换速度慢。
线程拥有独立的堆栈空间,但是共享数据段。
堆栈空间:又叫运行时段,用来存放所有局部变量和临时变量。
进程在保护模式下挂掉,不会影响其他进程,但是一个线程挂掉了,整个进程就挂掉了。
进程分为实模式和保护模式。
(实模式:程序地址为真实的物理地址,可以访问任意地址空间,这样不同进程可能访问到其它进程程序,造成严重错误
保护模式:程序地址为虚拟地址,然后由OS系统管理内存访问权限,这样每个进程只能访问分配给自己的物理内存空间,保证了程序的安全性
现在基本都是保护模式。)
进程之间的通信(1、管道。2、信号。3、消息队列。4、共享内存)
需要频繁创建与删除,优先选择线程。
对于cpu来说,系统效率上线程更加占优势。

场景:对java来说,上百个客户端访问tomcat,把后续处理丢给线程解决,这个线程最后是调用servlet程序。
如果不启用多线程,tomcat串行处理非常慢。


数据共享,防止死锁的情况。
线程过多的话,会影响性能,因为操作系统需要在他们之间切换。
线程与进程的调度由操作系统决定,程序自己不能决定什么时候执行,执行多长时间。
线程5个状态:创建、就绪、运行、阻塞、	死亡。
线程:主线程、子线程、守护线程、前台线程
进程与线程之间创建、切换的成本不同。


进程与线程的区别,这些区别是什么造成的:
1、对资源的占用率不同,进程是复制内存,而线程是共享数据,然后拥有独立的堆栈空间。
2、安全性不同,保护模式下进程在主进程挂掉后,其剩下的进程还能接着工作,而线程死掉一个就全部挂掉了,多进程可靠性更好。
3、在进程切换的时候,资源开销比较大,效率会相对差一些。线程的划分尺度小于进程,使得多线程程序的并发性高。
4、进程之间由于不共享,需要通信:1、队列。2、管道。3、信号量、4、共享内存。同步与互斥在写的时候容易挂掉。
python中,i/o密集多线程,cpu/计算密集用多进程。
多进程相比来说,其编程简单,调试简单
Hadoop MapReduce采用多进程
spark采用多线程。
还是各有优缺

GIL全局解释器锁:

python解释器默认每次只允许一个线程执行
某个线程想要执行,必须拿到GIL,python使用GIL的时候,调用的是c语言的原生线程,但是GIL。
互斥锁本质上,是把程序从并行变成串行,控制同一时间内共享数据只能被一个任务所修改。
1、拿到公共数据。
2、申请GIL。
3、python调用os原生的线程。
4、os操作cpu执行
5、当线程执行时间到了以后,无论是否完成,释放GIL
6、重复该过程
python对cpu密集型(循环,计算)不那么友好,因为切换线程需要时间
python对io密集型比较友好(网络爬虫、文件读写处理),
单核单线程在等待io操作的时候,会需要等待io的结果。
单核多线程在等待io操作的时候,就会切到另一个线程,那么系统执行效率就上来了。
python中充分利用多核cpu就使用多进程,每个进程会有自己的GIL
GIL中,python里一个进程永远只能同时执行一个线程
python中多进程适用于计算密集型,多线程适用于i/o密集型,比如一直print()
i/o密集:文件读写、web请求、数据库请求
互斥锁只允许一个线程同时访问数据,但是信号量允许多个。
python中创建线程的方式:
1、threading.Thread(target = work())#直接调包来用
2、class A(Thread,) #继承
线程本质上是用cpu时间切片,但是线程切换需要时间。
线程安全:
线程安全的函数应该为每个调用它的线程分配专门的空间,来储存需要单独保存的状态,
如果一个资源的创建,使用,销毁都在同一个线程内完成,且永远不会脱离该线程的控制,则该资源的使用就是线程安全的。
队列是线程安全的,对所有操作来说。
python本身很少使用多线程。Python 内置类型 dict,list ,tuple 是线程安全的。

Example:

# -*- coding = utf-8 -*-
import threading
import time

def test1(name):
    for i in range(0,5):
        time.sleep(2)
        print('线程'+str(name))

def test2(name):
    for i in range(0,5):
        time.sleep(1)
        print('线程'+str(name))
#  t1 = threading.Thread(target=test1)也可以
t1 = threading.Thread(target=test1, name="Job1", args=(123,))  # 创建开始线程
t2 = threading.Thread(target=test2, name="Job2", args=(321,))  # 创建开始线程

t1.start()  # 执行线程
t2.start()

多进程:

python中的多进程:
一种是进程池、一种是multiprecess、一种是fork()创建进程:
常见的apache就是由父进程监听端口,每当有新的http请求的时候,fork出子进程处理。
进程池:如果进程池的进程开满了,该请求就会等待,直到池中有进程结束,才会用之前的进程来执行新的任务。
进程池限制我们的计算机在一个自己可承受的范围内去并发地执行任务
进程中的join是指,主进程需要等待子进程完成后再进行后面的,也就是使得主进程阻塞。
# -*- coding = utf-8 -*-
import multiprocessing #跨平台的多进程包

def work():
    res = 0
    for i in range(10000000):
        res += i
    print(res)

def work2():
    res = 0
    for i in range(1000000):
        res += i
    print(res)

if __name__ == "__main__":

    p = multiprocessing.Process(target = work2)
    p.start()

    p = multiprocessing.Process(target = work)
    p.start()

进程池:

from multiprocessing import Pool
import os, time, random

def long_time_task(name):
    print('Run task %s (%s)...' % (name, os.getpid()))
    start = time.time()
    time.sleep(random.random() * 3)
    end = time.time()
    print('Task %s runs %0.2f seconds.' % (name, (end - start)))

if __name__=='__main__':
    print('Parent process %s.' % os.getpid())
    p = Pool(4) #创建大小为4的进程池
    for i in range(5): #4核,创建了5个任务,那么最后一个需要等一个跑完再跑。
        p.apply_async(long_time_task, args=(i,)) #异步

    print('Waiting for all subprocesses done...')
    p.close() #close就是不能再加进程了 
    p.join()# 等子进程全部结束后,主进程再执行
    print('All subprocesses done.')

进程间的通信:

python中用queue,pipe来实现通信,queue,就是说给队列通过put放入一个数据,然后另外一个通过get就可以取得。

或者用管道,单向。

队列并不是单向的。

from multiprocessing import Process, Pipe

def f(conn):
   conn.send([42, None, 'hello'])
   conn.close()

if __name__ == '__main__':
#每个对象都有两个方法 - send()和recv(),以在进程之间进行通信。
   parent_conn, child_conn = Pipe()  #一次创建需要2个参数来接,一个是发送一个是接受
   p = Process(target = f, args = (child_conn,))
   p.start()
   print (parent_conn.recv())
   p.join()

进程池:

from multiprocessing import Pool
import os, time, random

def long_time_task(name):
    print('Run task %s (%s)...' % (name, os.getpid()))
    start = time.time()
    time.sleep(random.random() * 3)
    end = time.time()
    print('Task %s runs %0.2f seconds.' % (name, (end - start)))

if __name__=='__main__':
    print('Parent process %s.' % os.getpid())
    p = Pool(4) #创建大小为4的进程池
    for i in range(5): #4核,创建了5个任务,那么最后一个需要等一个跑完再跑。
        p.apply_async(long_time_task, args=(i,)) #异步

    print('Waiting for all subprocesses done...')
    p.close() #close就是不能再加进程了 
    p.join()# 等子进程全部结束后,主进程再执行
    print('All subprocesses done.')

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值