知行合一
Choose a quality life!

一,线程               ====> 线程由代码段,数据段,TCB段组成

  线程被称作轻量级的进程        

        GIL:全局解释锁(只有Cpython解释器才有)对于线程来说,同一时间只允许一个线程访问cpu,因为有了GIL,所以没有真正的并行

  进程是资源分配的基本单位,线程是计算机可执行基本单位

  线程不可以自己独立拥有资源,线程的执行,必须依赖于所属进程中的资源

  进程中必须至少应该有一个线程

  线程分用户级线程和内核级线程:

  用户级线程:对于程序员来说的,这样的线程完全被程序员控制执行调度

  内核级线程:对于计算机内核来说的,这样的线程完全被内核控制调度。

二,     进程与线程的区别

      (1)cpu切换进程要比cpu切换线程 慢很多,在python中,如果IO操作过多的话,使用多线程最好了

    (2)在同一个进程内,所有线程共享这个进程的pid,

      也就是说所有线程共享所属进程的所有资源(包括进程中的全局变量)和内存地址

          (3)因为有GIL锁的存在,在Cpython中,没有真正的线程并行。但是有真正的多进程并行,当你的任务是计算密集的情况下,使用多进程好

             总结:在CPython中,IO密集用多线程,计算密集用多进程

     (4)关于守护线程和守护进程的事情(注意:代码执行结束并不代表程序结束)

           守护进程:要么自己正常结束,要么根据父进程的代码执行结束而结束      ======>    注意:父进程的代码执行完毕,守护进程就不执行

         守护线程:要么自己正常结束,要么根据父线程的执行结束而结束      ======>   注意:主线程的执行结束(包含普通线程执行结束),守护线程也结束

三, 线程的使用方法

  (1)递归锁用来解死锁,在单个线程内每获取一次锁会加计数一次,,释放一次会减计数一次,为零时就释放完毕

from threading import RLock
import time
def wc():
    r1.acquire()   #  小明获得厕所资源    注意这个锁是个整体,里面如果有小锁
    time.sleep(1)                         #那么小锁用完必须释放在大锁内
    print('小明在厕所解手')
    r2.acquire()                       #小明拿到纸
    print('小明拿到卫生纸了')
    r2.release()                       #小明释放纸
    r1.release()                #  小明释放厕所资源

def pap():
    r2.acquire()
    time.sleep(1)
    print('小雪在厕所解手')
    r1.acquire()
    print('小雪拿到卫生纸了')
    r1.release()
    r2.release()


if __name__ == '__main__':
    r1 = r2 = RLock()                   #配了两把锁
    p1 = Thread(target=wc,args=())      #开启一个上厕所的线程
    p2 = Thread(target=pap,args=())     #开启一个纸的线程
    p1.start()
    p2.start()
View Code

  (2)定时器(定时去执行任务)

from threading import Timer# 定时器


def func():
    print('就是这么nb!')

Timer(2.5,func).start()
# Timer(time,func)
# time:睡眠的时间,以秒为单位
# func:睡眠时间之后,需要执行的任务
View Code

  (3)信号量(一次性执行几个线程)

from threading import Semaphore,Thread
import time


def func(sem,i):
    sem.acquire()
    print('第%s个人进入屋子'%i)
    time.sleep(2)
    print('第%s个人离开屋子'%i)
    sem.release()


sem = Semaphore(20)
for i in range(20):
    t = Thread(target=func,args=(sem,i))
    t.start()
View Code

  (4)事件

from threading import Thread,Event
import time,random

def conn_mysql(e,i):
    count = 1         #计数
    while count <= 3:
        if e.is_set():     #接收e.set()的值,如果事件是真则执行
            print('第%s个人连接成功!'%i)
            break
        print('正在尝试第%s次重新连接...'%(count))
        e.wait(0.5)
        count += 1

def check_mysql(e):
    print('\033[42m 数据库正在维护 \033[0m')
    time.sleep(random.randint(1,2))
    e.set()


if __name__ == '__main__':
    e = Event()                  #创建一个时间对象
    t_check = Thread(target=check_mysql,args=(e,))     #注意:将时间对象传给执行函数
    t_check.start()              #起动执行检查数据库的任务
    for i in range(10):
        t_conn = Thread(target=conn_mysql,args=(e,i))   #注意:将时间对象传给执行函数
        t_conn.start()           #启动执行10个线程对象去执行连接数据库任务
View Code

  (5)条件(可以控制线程的多少,线程数执行完毕就结束)

from threading import Thread,Condition
import time
# Condition涉及4个方法
# acquire()
# release()
# wait()    是指让线程阻塞住
# notify(int)  是指给wait发一个信号,让wait变成不阻塞
#     int是指,你要给多少给wait发信号


def func(con,i):
    con.acquire()
    con.wait()# 线程执行到这里,会阻塞住,等待notify发送信号,来唤醒此线程
    con.release()
    print('第%s个线程开始执行了!'%i)

if __name__ == '__main__':
    con = Condition()
    for i in range(10):
        t = Thread(target=func,args=(con,i))
        t.start()
    while 1:
        con.acquire()
        num = int(input(">>>"))
        con.notify(num)# 发送一个信号给num个正在阻塞在wait的线程,让这些线程正常执行
        con.release()
View Code

 四,,线程池

from concurrent.futures import ProcessPoolExecutor,ThreadPoolExecutor
#不管是ProcessPoolExecutor的进程池  还是Pool的进程池,回调函数都是父进程调用的。
#  concurrent.futures 这个模块是异步调用的机制
#  concurrent.futures 提交任务都是用submit
#  for + submit 多个任务的提交
#  shutdown 是等效于Pool中的close+join,是指不允许再继续向池中增加任务,然后让父进程(线程)等待池中所有进程执行完所有任务。
#进程池和线程池不需要用启动start
def func1(num):
    sum = 0
    for i in range(num):
        sum += i ** 2
    return sum

def back_func1(res):             # 回调函数对数据二次处理,一般会写入文件截取处理后的数据,无法return返回值
    with open('aa','a',encoding='utf-8') as f1:
        f1.write(str(66 * (res.result()))+'\n')

#一,关于线程的使用事项方法
if __name__ == '__main__':
    #(1)用map函数创建多个线程
    t = ThreadPoolExecutor(20)
    lis = []
    ret = map(func1,range(200))#,开启多个线程:相当于for循环加上submit,,返回的是个生成器,用for循环去取值
    for i in ret:
        lis.append(i)
    print(len(lis),lis)
    #(2)用submit函数创建多个线程
    t1 = ThreadPoolExecutor(20)
    for i in range(200):
        t1.submit(func1,i).add_done_callback(back_func1)           #异步时,回调函数被主线程
        # ret = t.submit(func1,i)       #开启一个线程第一个参数是函数,第二个是参数(是个元组类型,args = (i,))可以简写
        # print(ret.result())            #注意返回值:用ret.result()来取值
    t1.shutdown()                       #这个命令的功能相当于t.close()  和t.join() 的功能,等待子线程执行结束

#二,关于进程的使用事项方法
    #(1)用map函数创建多个进程
if __name__ == '__main__':
    #(1) 用sumit函数创建多个进程:
    p = ProcessPoolExecutor(5)
    for i in range(100):
        # p.submit(func1,i).add_done_callback()              #异步时,回调函数被主进程调用
        ret = p.submit(func1, args = (i,))   ##开启一个进程第一个参数是函数,第二个是参数(是个元组类型,args = (i,))可以简写
        print(ret.result())                    #注意返回值:用ret.result()来取值
    p.shutdown()                            # 这个命令的功能相当于t.close()  和t.join() 的功能,等待子进程执行结束
    #(2)用map函数创建多个进程:
    p1 = ProcessPoolExecutor(20)
    lis = []
    ret = map(func1,range(200))#,开启多个进程:相当于for循环加上submit,,返回的是个生成器,用for循环去取值
    for i in ret:
        lis.append(i)               #这个操作也相当于等待所有的任务执行结束  相当于执行t.shutdown()
    print(len(lis),lis)
View Code

 

  

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

  

posted on 2018-08-23 17:07  小米没吃饭  阅读(260)  评论(0编辑  收藏  举报