python-day17

进程与线程:

1、什么是进程?
进程是程序的一次动态执行过程。每个进程都拥有自己的地址空间、内存、数据栈以及其他用于跟踪执行的辅助数据
操作系统负责其上所有进程的执行,操作系统会为这些进程合理地分配执行时间。
2、什么是线程?
线程是操作系统能够进行运算调度的最小单位。它被包含在进程之中,是进程中的实际运作单位。
一条线程指的是进程中一个单一顺序的控制流,一个进程中可以并发多个线程,每条线程并行执行不同的任务。
一个线程是一个execution context(执行上下文),即一个cpu执行时所需要的一串指令。

假设你正在读一本书,没有读完,你想休息一下,但是你想在回来时恢复到当时读的具体进度。
有一个方法就是记下页数、行数与字数这三个数值,这些数值就是execution context。
如果你的室友在你休息的时候,使用相同的方法读这本书。
你和她只需要这三个数字记下来就可以在交替的时间共同阅读这本书了。

线程的工作方式与此类似。CPU会给你一个在同一时间能够做多个运算的幻觉,实际上它在每个运算上只花了极少的时间,
本质上CPU同一时刻只干了一件事。它能这样做就是因为它有每个运算的execution context。
就像你能够和你朋友共享同一本书一样,多任务也能共享同一块CPU。

3、什么是主线程?
主线程就是创建进程中产生的第一个线程,也就是main函数对应的线程。

4、线程ID
线程是一个轻量级进程,每一个用户态的线程 ,在内核中都对应这一个调度实体,也拥有着自己 的进程描述符。
tgid:线程组ID,线程组中的每一个线程的tgid都是相同的,在外表现为进程ID,它等于主线程的ID 。
tid:每一个线程在自己的用户层面上都有一个私有的pid,可以通过tid找到自己的虚拟地址,再通过页表映射到物理地址空间。在用户层面上展现的是线程的ID(tid),但在内核中它实际上是一个轻量级进程(pid)。

5、多线程
操作系统通过给不同的线程分配时间片(CPU运行时长)来调度线程,当CPU执行完一个线程的时间片后就会快速切换到下一个线程,
时间片很短而且切换切速度很快以至于用户根本察觉不到。早期的计算机是单核单线程的,
多个线程根据分配的时间片轮流被CPU执行,如今绝大多数计算机的CPU都是多核的,
多个线程在操作系统的调度下能够被多个CPU并发执行,程序的执行速度和CPU的利用效率大大提升。
绝大多数主流的编程语言都能很好地支持多线程,然而python由于GIL锁无法实现真正的多线程。

Process类:

1、创建进程:
  1. p = Process(target=callable,name='',args='',kwargs='')
  2. p.start() 启动进程

2、
主进程:执行的时候,默认的进程称作主进程
子进程:在主进程中可以开启子进程
p1 = Process(target=callable,args='')
os.getpid() 当前进程
os.getppid() 父进程

3、全局变量
如果是全局变量则,每个进程都会拥有一份全局变量。各自操作各自的全局变量

4、阻塞主进程
子进程.join()
阻塞主进程后面的代码

python-day17
import time
import os
from multiprocessing import Process


# print('----->top:', os.getpid())

def task1():
    global number
    for i in range(5):
        print('洗衣服:', i, os.getpid(), os.getppid())
        time.sleep(0.5)
    number -= 10
    print('洗衣服:', number)


def task2(n):
    global number
    for i in range(n):
        print('劳动最光荣,扫地中...', i, os.getpid(), os.getppid())
        time.sleep(0.5)
    number -= 8
    print('扫地:', number)


number = 100
if __name__ == '__main__':
    print('main:--->start', os.getpid())
    p1 = Process(target=task1)
    p2 = Process(target=task2, args=(6,))

    p1.start()
    p2.start()

    # join() 加入   阻塞主进程后面的代码不执行
    p1.join()
    p2.join()

    print('main:--->end', os.getpid())
    print('main中的number是:', number)
View Code

5、进程对象可以访问的方法:

run()
start()        开始进程
join()         阻塞进程
terminate()     终止进程
close()      目标完成之后调用的close()释放资源
is_live       判断target任务是否完成,如果任务完成则False

python-day17
import time
import os
from multiprocessing import Process


def task1():
    for i in range(5):
        print('洗衣服:',i,os.getpid(),os.getppid())
        time.sleep(0.5)

def taxk2(n):
    for i in range(n):
        print('劳动最光荣:...',i,os.getpid(),os.getppid())
        time.sleep(0.5)

if __name__=='__main__':
    p1 = Process(target=task1)
    p2 = Process(target=taxk2,args=(6,))

    # p1.run()
    # p2.run()
    p1.start()
    p2.start()

    for i in range(10):
        if i==4:
            p1.terminate()   # 终止p1进程
        elif i==5:
            pass
            # p2.terminate()
            # p2.close()    # 3.7中才能用
        time.sleep(0.4)
        print('main:',i)


    print('p1:',p1.is_alive())
View Code

 6、进程池:Pool

阻塞式和非阻塞式:

python-day17
import os
import time
from multiprocessing import Pool


def task1():
    print('洗衣服:', os.getpid(), os.getppid())
    time.sleep(0.5)
    return '我是进程:' + str(os.getpid())
    # response = requests.get(url)
    # return response.content


def callback(msg):
    print('{}洗衣服任务完成!'.format(msg))
    # 保存下载的文件到本地


if __name__ == '__main__':
    pool = Pool(4)
    # 非阻塞式
    for i in range(10):
        pool.apply_async(task1, callback=callback)

    # 添加任务结束
    pool.close()
    # 阻塞主进程
    pool.join()

    print('main over')
非阻塞式 python-day17
import os
import time
from multiprocessing import Pool


def task1():
    for i in range(5):
        print('洗衣服:',i, os.getpid(), os.getppid())
        time.sleep(0.5)
        # return '我是进程:' + str(os.getpid())


if __name__ == '__main__':
    pool = Pool(4)
    #
    for i in range(10):
        pool.apply(task1) # 阻塞式: 进程池中一个任务完成之后才能做下一个任务
        print('------------------------->',i)

    # 添加任务结束
    pool.close()
    # 阻塞主进程
    pool.join()

    print('main over')
阻塞式

 7、进程间通信和信号量

队列:FIFO

put() : 队列中存放,如果满了则阻塞
get() : 从队列中取值,如果空了则阻塞

full() 判断是否是满了
empty() 判断是否是空了 如果空了,True
qsize() 获取长度

python-day17
from multiprocessing import Process, Queue

queue = Queue(3)

queue.put('馒头1')
queue.put('馒头2')
queue.put('馒头3')
print(queue.full())  # 判断是否满了
print(queue)
print(queue.qsize())

try:
    queue.put('馒头4', timeout=3)
    queue.put('馒头5', timeout=3)
except:
    print('存放馒头失败')

while True:
    try:
        print(queue.get(timeout=1))
    except:
        print('队列为空,取不出东西')
        break
View Code

例子:爬取图片

python-day17
'''
https://timgsa.baidu.com/timg?image&quality=80&size=b10000_10000&sec=1565782454&di=03886bb0af9fbd9d41896cea3fb184e2&src=http://img3.doubanio.com/view/photo/l/public/p2510470342.jpg
https://ss1.bdstatic.com/70cFvXSh_Q1YnxGkpoWK1HF6hhy/it/u=3940930810,2726038075&fm=26&gp=0.jpg
https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1565792399028&di=4a4ca85c5d2bae57fcde446658199a5c&imgtype=0&src=http%3A%2F%2Fn.sinaimg.cn%2Fent%2Fw880h495%2F20171213%2Ff41w-fypsqiz4586695.jpg
https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1565792399024&di=4bedcbf80f19d5327ecff7ae6f1a9a13&imgtype=0&src=http%3A%2F%2Fimg.mp.itc.cn%2Fupload%2F20170619%2F3aa9d9dcc80141908b641e212ee9b846_th.jpg
'''
import requests
import time
from multiprocessing import Process, Queue


def download(url, queue):
    for image in url:
        response = requests.get(image)
        image_da = response.content
        queue.put(image_da)


def save_file(queue):
    count = 0
    while True:
        try:
            data = queue.get(timeout=5)
            filename = 'img' + str(count)+'.jpg'
            with open('images/'+filename, 'wb') as ws:
                ws.write(data)
            count+=1
            print('保存{}完毕!'.format(filename))
        except Exception as err:
            print('沒有更多数据了')
            break



if __name__ == '__main__':
    q1 = Queue(6)
    images = [
        'https://timgsa.baidu.com/timg?image&quality=80&size=b10000_10000&sec=1565782454&di=03886bb0af9fbd9d41896cea3fb184e2&src=http://img3.doubanio.com/view/photo/l/public/p2510470342.jpg',
        'https://ss1.bdstatic.com/70cFvXSh_Q1YnxGkpoWK1HF6hhy/it/u=3940930810,2726038075&fm=26&gp=0.jpg',
        'https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1565792399028&di=4a4ca85c5d2bae57fcde446658199a5c&imgtype=0&src=http%3A%2F%2Fn.sinaimg.cn%2Fent%2Fw880h495%2F20171213%2Ff41w-fypsqiz4586695.jpg',
        'https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1565792399024&di=4bedcbf80f19d5327ecff7ae6f1a9a13&imgtype=0&src=http%3A%2F%2Fimg.mp.itc.cn%2Fupload%2F20170619%2F3aa9d9dcc80141908b641e212ee9b846_th.jpg'
    ]
    p1 = Process(target=download, args=(images, q1))
    p2 = Process(target=save_file, args=(q1,))

    a = time.time()

    p1.start()
    p2.start()

    p1.join()
    p2.join()
    b = time.time()
    cha = b - a
    print(cha)
View Code

 8、自定义进程

python-day17
import time
import os
import requests
from multiprocessing import Process, Queue

class DownloadProgress(Process):
    def __init__(self,urls,queue):
        Process.__init__(self)
        self.urls = urls
        self.queue = queue

    # 重新父类的run方法
    def run(self):
        for image in self.urls:
            filename = os.path.split(image)[1]
            response = requests.get(image)
            data = response.content
            self.queue.put(data)
            self.queue.get()
            print('下载{}完毕!!'.format(filename))
        self.queue.close()



if __name__ == '__main__':
    q1 = Queue(4)
    images = [
        'https://ss3.bdstatic.com/70cFv8Sh_Q1YnxGkpoWK1HF6hhy/it/u=4151794834,2429338731&fm=26&gp=0.jpg',
        'https://ss2.bdstatic.com/70cFvnSh_Q1YnxGkpoWK1HF6hhy/it/u=3247242571,2598044801&fm=26&gp=0.jpg',
        'https://ss1.bdstatic.com/70cFvXSh_Q1YnxGkpoWK1HF6hhy/it/u=3639290340,1034325833&fm=26&gp=0.jpg',
        'https://ss0.bdstatic.com/70cFvHSh_Q1YnxGkpoWK1HF6hhy/it/u=1853363442,2878778500&fm=26&gp=0.jpg'
    ]
    dlprocess = DownloadProgress(images,q1)
    dlprocess.start()

    dlprocess.join()
    print('main')
View Code

线程

python 如何创建一个线程?
线程:
1. t1 = Thread(target=task1)
2. 自定义线程

run()
start()
join()
name:           默认的Thread-1, Thread-2,....
current_thread().name      获取当前线程的名字
is_alive

python-day17
import os
import time
from threading import Thread,current_thread


def task1():
    for i in range(5):
        print('{}洗衣服:'.format(current_thread().name), i, os.getpid(), os.getppid())
        time.sleep(0.5)


def task2(n):
    for i in range(n):
        print('{}劳动最光荣,扫地中...'.format(current_thread().name), i, os.getpid(), os.getppid())
        time.sleep(0.5)


if __name__ == '__main__':
    print('main:', os.getpid())
    # 创建线程对象
    t1 = Thread(target=task1,name='警察')
    t2 = Thread(target=task2,name='小偷', args=(6,))
    # 启动线程
    t1.start()
    t2.start()

    # t1.join()
    # t2.join()

    for i in range(3):
        print("t1:", t1.is_alive())
        print("t2:", t2.is_alive())
        print('main:', i)
        time.sleep(1)
View Code

自定义线程:
1. 定义一个类继承Thread
2. 重写: [__init__] 必须重写: run()
3. 创建线程类对象
4. 启动线程

python-day17
import time
from threading import Thread


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

    def run(self):
        for i in range(5):
            print('{}正在打印:{}'.format(self.name, i))
            time.sleep(0.1)


if __name__ == '__main__':
    t1 = MyThread('小明')
    t2 = MyThread('小花')
    t3 = MyThread('ergou')

    t1.start()
    t2.start()
    t3.start()
View Code

共享数据:
如果有全局变量,则每个线程是共享的
GIL:共享锁

python-day17
import time
from threading import Thread,current_thread

# 全局变量
ticket = 10

def sale_ticket():
    global ticket
    while True:
        if ticket>0:
            print('{}正在卖{}张火车票!!'.format(current_thread().name,ticket))
            ticket-=1
            time.sleep(1)
        else:
            break

if __name__ == '__main__':
    t1 = Thread(target=sale_ticket,name='1号窗口')
    t2 = Thread(target=sale_ticket,name='2号窗口')
    t3 = Thread(target=sale_ticket,name='3号窗口')

    t1.start()
    t2.start()
    t3.start()
View Code python-day17
from threading import Thread, Lock

number = 0


def task(lock):
    global number
    lock.acquire()  # 握住
    for i in range(100000):
        number += 1
    lock.release()  # 释放锁


if __name__ == '__main__':
    lock = Lock()
    t1 = Thread(target=task, name='1号窗口', args=(lock,))
    t2 = Thread(target=task, name='2号窗口', args=(lock,))
    t3 = Thread(target=task, name='3号窗口', args=(lock,))

    t1.start()
    t2.start()
    t3.start()

    t1.join()
    t2.join()
    t3.join()

    print('number:', number)
View Code

 

上一篇:简单练习4(Day17、18 JAVA复习)


下一篇:Day17 计算文件夹的大小、os模块、shtil模块、zipfile、tarfile