线程&线程池

线程

进程和线程:

  进程只是用来把资源集中到一起(进程只是一个资源单位,或者说资源集合),而线程才是cpu上的执行单位。

注意:两个都是过程

 

线程一个特点:

  一个进程中,多个线程共享资源

 

线程和进程区别:

  1. 线程的创建开销小于进程, 创建速度快

  2. 同一进程下的多个线程共享该进程的地址空间(资源)

 

主线程影响其他线程:

  是因为线程的概念:

  一个进程内默认就会有一个控制线程,该控制线程可以执行代码从而创建新的线程,

  该控制线程的执行周期就代表该进程的执行周期

 

1.一个进程内不开子进程,也不开'子线程' : 主线程结束,该进程就结束

2.当一个进程内开启子进程时 : 主线程结束,主进程要等,等所有子进程运行完毕,给儿子收尸.

3.当一个进程内开启多个线程时 :

  主线程结束并不意味着进程结束,

  进程的结束指的是该进程内所有的线程都运行完毕,才应该回收进程.

 

简单点说,就是:

  主进程等子进程, 是主进程活都干完了

  主线程等子线程,是主进程还没有干完活

  (所有的线程都干完活了,该进程才算干完活了)

 

子进程和线程的id号 验证内存空间

import os
from multiprocessing import Process

n = 100
def func():
    global n
    n = 1
    print('',n,os.getpid())


if __name__ == '__main__':
    p = Process(target=func,)
    p.start()
    print('',n,os.getpid())
# 主 100 8852        主进程和子进程的id号不一样,说明进程间的内存空间是隔离的
# 子 1 4716          子进程修改变量,主进程感觉不到,说明进程间的内存空间是隔离的

全局变量的修改,验证内存空间

import os
from threading import Thread

n = 100


def func():
    global n
    n = 1
    print(current_thread().getName(), n, os.getpid())


if __name__ == '__main__':
    t = Thread(target=func,name = ‘func’)       #给子线程起名字,不写,默认 Thread-1.Thread-2.....
    t.start()
    print(current_thread().getName(), n, os.getpid())
    
# func 1 1088          子线程和主线程的id号一样,说明主线程和子线程在一个进程空间里
# MainThread 1 1088          子线程修改变量n = 1,主线程能感知到,说明线程间共享数据

进程可以看进程号

线程只能看名字了

getName() 测试的时候用的较多

 

enumerate()    以列表的形式列出当前所有活着的线程

active_count()    统计当前所有活着的线程的个数

import time
from threading import Thread, active_count, enumerate       需要导入threading模块中这两个方法


def func():
    time.sleep(3)


if __name__ == '__main__':
    t = Thread(target=func)
    t.start()
    print(enumerate())
    print(active_count())

# [<_MainThread(MainThread, started 9212)>, <Thread(Thread-1, started 1612)>]   列表形式
# 2 

-------------------------------------

print(enumerate()[0].getName())         #enumerate()是一个列表,取索引值[0],再通过getName(),一样能拿到线程的名字
print(current_thread().getName())
#MainThread
#MainThread

os.cpu_count()    查看cpu核数

import os
print(os.cpu_count())

线程池

t = ThreadPoolExecutor()    创建线程池的时候,如果不设置数量,默认是cpu核数*5

证明线程池不设置参数,默认值 是cpu核数*5

import time
import os
from threading import ac-tive_count
from concurrent.futures import ThreadPoolExecutor


def func():
    time.sleep(1)


if __name__ == '__main__':
    t = ThreadPoolExecutor()            #创建线程池的时候,如果不设置数量,默认是cpu核数*5
    for i in range(100):
        t.submit(func,)
    print(os.cpu_count())       #cpu核数
    print(active_count())       #子线程数 + 主线程

#4
#21   

进程池不要超过cpu核数的2倍(不是真理,具体会根据程序问题的增多,数目相应调大,也和电脑的性能有关)

这个参数肯定不能再程序里面写死,要写到配置文件里,要让软件的使用者( 给运维人员,不是直接给客户)根据他自己的业务场景以及他硬件的性能,要做相应的调整,取配置,所有的支

持并发的软件都是这样做,都应该这样做.不应该在程序里面写死.

多线程+异步调用+requests

requests 库

HTTP客户端库(编写爬虫和测试服务器响应数据时经常用到)

安装:pip install requests 

get 请求:

  r = requets.get('http://www.baidu.com')

  r.text 返回headers中的编码解析的结果

 

异步调用:

  (回调机制:任务执行完,触发回调函数,同时把任务结果传给回调函数)

  提交完任务,(为该任务绑定一个回调函数),不用在原地等任务执行完毕拿到结果,可以直接提交下一个任务.

  一个任务一旦执行完毕就会自动触发回调函数的运行

不在原地等结果,那么结果哪里去了?

  就是通过回调机制

 

回调函数:add_done_callback(func) 

  回调函数的参数是单一的:

  回调函数的参数就是它所绑定任务的返回值(绑定任务的return)

 

异步调用,不用等结果 结果去哪了 就是 回调

import requests
import time
from threading import current_thread
from concurrent.futures import ThreadPoolExecutor

url = ['https://www.baidu.com',
       'https://www.jd.com',
       'https://www.python.org',
       'http://www.qq.com']


def get(url):
    print('get',current_thread().getName(),url)     #current_thread().getName()打印哪些线程执行下载任务
    time.sleep(2)
    r = requests.get(url)
    if r.status_code == 200:                # 200代表成功下载
        return {'url': url, 'text': r.text}


def parse(obj):
    res = obj.result()
    print('parse',current_thread().getName(),res['url'],len(res['text']))       #打印哪些线程执行解析任务


if __name__ == '__main__':
    t = ThreadPoolExecutor(2)
    for i in url:
        t.submit(get,i).add_done_callback(parse)        #回调函数
    t.shutdown(wait=True)
    print(current_thread().getName())

# get ThreadPoolExecutor-0_0 https://www.baidu.com
# get ThreadPoolExecutor-0_1 https://www.jd.com
# parse ThreadPoolExecutor-0_0 https://www.baidu.com 2443
# get ThreadPoolExecutor-0_0 https://www.python.org
# parse ThreadPoolExecutor-0_1 https://www.jd.com 124541
# get ThreadPoolExecutor-0_1 http://www.qq.com
# parse ThreadPoolExecutor-0_1 http://www.qq.com 235464
# parse ThreadPoolExecutor-0_0 https://www.python.org 48856
# MainThread

一直是由线程控制处理

 

通过进程演示异步概念(不用开新的进程,主进程处理结果)

import requests
import time
import os
from threading import Thread, current_thread
from concurrent.futures import ThreadPoolExecutor, ProcessPoolExecutor

url = ['https://www.baidu.com',
       'https://www.jd.com',
       'https://www.python.org',
       'http://www.qq.com']


def get(url):
    print('get',os.getpid(),url)            #os.getpid()打印哪些进程执行下载任务
    time.sleep(3)
    r = requests.get(url)
    if r.status_code == 200:
        return {'url': url, 'text': r.text}


def parse(obj):
    res = obj.result()
    print('parse',os.getpid(),res['url'],len(res['text']))      #os.getpid()打印哪些进程执行解析任务


if __name__ == '__main__':
    t = ProcessPoolExecutor(2)
    for i in url:
        t.submit(get,i).add_done_callback(parse)
    t.shutdown(wait=True)
    print('\n',os.getpid())

# get 6972 https://www.baidu.com
# get 7180 https://www.jd.com
# get 6972 https://www.python.org
# parse 7856 https://www.baidu.com 2443
# get 7180 http://www.qq.com
# parse 7856 https://www.jd.com 124541
# parse 7856 http://www.qq.com 235464
# parse 7856 https://www.python.org 48856
#
#  7856

 

posted @ 2017-10-28 23:20  静静别跑  阅读(188)  评论(0编辑  收藏  举报