39 day of python

今日内容:

线程的Thread模块

同步控制:锁 事件 信号量 条件 定时器 队列

线程池/进程池

 

协程

网络开发中的IO模型

 

池的概念

回调函数:Pool这个类中  回调函数是主进程执行

  如果有两个任务,我的第二个任务在第一个任务执行完毕之后能够立即被主进程执行

线程的概念

  进程是操作系统中最小的资源分配单位

  线程是CPU调度的最小单位

  线程进程之间的对比

    线程不能独立存在,必须在一个进程里

    线程的开启 关闭以及切换的开销要远远小于进程

  同一个进程之间的多个线程之间数据共享

  全局解释器锁GIL

    使得一个进程中的多个线程不能充分的利用多核

如何开启一个线程

 

进程好  还是  线程好?

什么时候用多进程  什么时候用多线程?

  并发

  CPU的使用率:计算       - 多进程

  IO操作:网络\文件\数据库   - 多线程

  

Thread类

import time
from threading import Thread
from multiprocessing import Process
def func(a):
    a = a + 1
if __name__ == '__main__':
    start = time.time()
    t_l = []
    for i in range(50):
        t = Thread(target=func,args=(i,))
        t.start()
        t_l.append(t)
    # t_l  50个线程对象
    for i in t_l:t.join()
    print('主线程')
    print(time.time() - start)

    start = time.time()
    t_l = []
    for i in range(50):
        t = Process(target=func,args=(i,))
        t.start()
        t_l.append(t)
    for i in t_l:i.join()
    print('主进程')
    print(time.time() - start)

start    join

terminate  在线程中没有

 

线程之间的数据共享

from threading import Thread
n = 100
def func():
    global n
    n -= 1
if __name__ == '__main__':
    t_l = []
    for i in range(100):
        t = Thread(target=func)
        t.start()
        t_l.append(t)
    for t in t_l:
        t.join()
    print(n)

 

守护线程

import time
from threading import Thread
def thread1():
    while True:
        print(True)
        time.sleep(0.5)

def thread2():
    print('in t2 start')
    time.sleep(3)
    print('in t2 end')

if __name__ == '__main__':
    t1 = Thread(target=thread1)
    t1.setDaemon(True)
    t1.start()
    t2 = Thread(target=thread2)
    t2.start()
    time.sleep(1)
    print('主线程')

 

主线程如果结束了  那么整个进程就结束

守护线程  会等待主线程结束之后才结束

  主进程  等待  守护进程  子进程

  守护进程  只守护主进程的代码就可以了

  守护线程不行  主线程如果结束了  那么整个进程就结束  所有的线程就能结束

例子

使用多线程实现tcp协议的socket server

 

 

开启线程的第二种方式和查看线程ID

from threading import Thread,get_ident
class MyThread(Thread):
    def __init__(self,args):
        super().__init__()  # Thread类的init,在这个方法中做了很多对self的赋值操作,都是给创建线程或者使用线程的时候用的
        self.args = args
    def run(self):
        print('in my thread : ',get_ident(),self.args)
print('main',get_ident())
t = MyThread('wahaha')
t.start()

 

线程中的方法

import time
from threading import Thread,get_ident,currentThread,enumerate,activeCount
class MyThread(Thread):
    def __init__(self,args):
        super().__init__()   
        self.args = args

    def run(self):
        time.sleep(0.1)
        print(currentThread())
        print('in my thread : ',get_ident(),self.args)

print('main',get_ident())
t = MyThread('wahaha')
print(t.is_alive())
t.start()
print(activeCount())  # 正在运行的线程的数量 len(enumerate())
main 4572
False
2
<MyThread(Thread-1, started 4924)>
in my thread :  4924 wahaha

 

Thread类
开启线程
传参数
join

和进程的差异
  效率
  数据共享
  守护线程

面向对象的方式开启线程
thread对象的其他方法 : isAlive ,setname,getname
threading模块的方法 : currentTread,activeCount,enumerate

Thread实例对象的方法
  # isAlive(): 返回线程是否活动的。
  # getName(): 返回线程名。
  # setName(): 设置线程名。

threading模块提供的一些方法:
  # threading.currentThread(): 返回当前的线程变量。
  # threading.enumerate(): 返回一个包含正在运行的线程的list。正在运行指线程启动后、结束前,不包括启动前和终止后的线程。
  # threading.activeCount(): 返回正在运行的线程数量,与len(threading.enumerate())有相同的结果。

 

在多个进程\线程同时访问一个数据的时候就会产生数据不安全的现象

多进程  访问文件

多线程

  同时去访问一个数据

GIL  全局解释器锁

  在用一个进程里的每一个线程同一时间只能有一个线程访问CPU

尽量不要设置全局变量

只要在多线程/进程之间用到全局变量  就加上锁

from threading import Lock,Thread
noodle = 100
def func(name,lock):
    global noodle
    lock.acquire()
    noodle -= 1
    lock.release()
    print('%s吃到面了'%name)

if __name__ == '__main__':
    lock = Lock()   # 线程锁  互斥锁
    t_lst = []
    for i in range(10):
        t = Thread(target=func,args=(i,lock))
        t.start()
        t_lst.append(t)
    for i in t_lst:
        t.join()
    print(noodle)

 

科学家吃面问题

import time
from threading import Thread,Lock
lock = Lock()
noodle_lock = Lock()
fork_lock = Lock()
def eat1(name):
    noodle_lock.acquire()
    print('%s拿到了面'% name)
    fork_lock.acquire()
    print('%s拿到了叉子'% name)
    print('%s在吃面'%name)
    time.sleep(0.5)
    fork_lock.release()   # 0.01
    noodle_lock.release()   # 0.01

def eat2(name):
    fork_lock.acquire()  # 0.01
    print('%s拿到了叉子' % name)  # 0.01
    noodle_lock.acquire()
    print('%s拿到了面'% name)
    print('%s在吃面'%name)
    time.sleep(0.5)
    noodle_lock.release()
    fork_lock.release()

eat_list = ['alex','wusir','太白','yuan']
for name in eat_list:  # 8个子线程 7个线程 3个线程eat1,4个线程eat2
    Thread(target=eat1,args=(name,)).start()
    Thread(target=eat2,args=(name,)).start()

以上方法会卡住,由此引出递归锁

 

递归锁

from threading import RLock
rlock = RLock()
rlock.acquire()
print(1)
rlock.acquire()
print(2)
rlock.acquire()
print(3)

 

递归锁解决死锁问题

import time
from threading import Thread,RLock
lock = RLock()
def eat1(name):
    lock.acquire()
    print('%s拿到了面'% name)
    lock.acquire()
    print('%s拿到了叉子'% name)
    print('%s在吃面'% name)
    time.sleep(0.5)
    lock.release()
    lock.release()

def eat2(name):
    lock.acquire()  # 0.01
    print('%s拿到了叉子' % name) # 0.01
    lock.acquire()
    print('%s拿到了面' %name)
    print('%s在吃面' %name)
    time.sleep(0.5)
    lock.release()
    lock.release()
eat_lst = ['alex','wusir','太白','yuan']
for name in eat_lst:    # 8个子线程 7个线程 3个线程eat1,4个线程eat2
    Thread(target=eat1,args=(name,)).start()
    Thread(target=eat2,args=(name,)).start()

 

互斥锁解决死锁问题

import time
from threading import Thread,Lock
lock = Lock()
def eat1(name):
    lock.acquire()
    print('%s拿到了面' % name)
    print('%s拿到了叉子' % name)
    print('%s在吃面'%name)
    time.sleep(0.5)
    lock.release() # 0.01

def eat2(name):
    lock.acquire()  # 0.01
    print('%s拿到了叉子' % name) # 0.01
    print('%s拿到了面' % name)
    print('%s在吃面'%name)
    time.sleep(0.5)
    lock.release()

eat_lst = ['alex','wusir','太白','yuan']
for name in eat_lst:  # 8个子线程 7个线程 3个线程eat1,4个线程eat2
    Thread(target=eat1,args=(name,)).start()
    Thread(target=eat2,args=(name,)).start()

死锁

  多把锁同时应用在多个线程中

互斥锁和递归锁哪个好

  递归锁  快速恢复服务

  死锁问题的出现  是程序的设计或者逻辑的问题

  还应该进一步的排除和重构逻辑来保证使用互斥锁也不会发生死锁

互斥锁和递归锁的区别

  互斥锁  就是在一个线程中不能连续多次acquire

  递归锁  可以在同一个线程中acquire任意次,注意acquire多少次就需要release多少次

 

信号量

  锁 + 计数器

import time
from multiprocessing import Semaphore,Process,Pool
def ktv1(sem,i):
    sem.acquire()
    i +=1
    sem.release()

def ktv2(i):
    i += 1

if __name__ == '__main__':
    sem = Semaphore(5)
    start = time.time()
    p_l = []
    for i in range(100):
        p = Process(target=ktv1,args=(sem,i))
        p.start()
        p_l.append(p)
    for p in p_l:
        p.join()
    print('###',time.time() - start)

    start = time.time()
    p = Pool(5)
    p_l = []
    for i in range(100):
        ret = p.apply_async(func=ktv2,args=(sem,i))
        p_l.append(ret)
    p.close()
    p.join()
    print('***',time.time() - start)

 

池 和 信号量

池 效率高

  池子里有几个一共就起几个

  不管多少任务  池子的个数是固定的

  开启进程和关闭进程这些事都是需要固定的开销

  就不产生额外的时间开销

  且进程池中的进程数控制得好,那么操作系统的压力也小

信号量

  有多少个任务就起多少个进程/线程

  可以帮助你减少操作系统切换的负担

  但是并不能帮助你减少进/线程开启和关闭的时间

 

事件

wait

  等到事件内部的信号变成True就不阻塞了

set

  设置信号变成True

clear

  设置信号变成False

is_set

  查看信号是否为True

 

数据库连接

import time
import random
from threading import Event,Thread
def check(e):
    '''检测一下数据库的网络和我的网络是否通'''
    print('正在检测两台机器之间的网络情况 ...')
    time.sleep(random.randint(1,3))
    e.set()

def connet_db(e):
    e.wait()
    print('连接数据库 ... ')
    print('连接数据库成功~~~')

e = Event()
Thread(target=connet_db,args=(e,)).start()
Thread(target=check,args=(e,)).start()

import time
import random
from threading import Event,Thread
def check(e):
    '''检测一下数据库的网络和我的网络是否通'''
    print('正在检测两台机器之间的网络情况 ...')
    time.sleep(random.randint(0,2))
    e.set()

def connet_db(e):
    n = 0
    while n < 3:
        if e.is_set():
            break
        else:
            e.wait(0.5)
            n += 1
    if n == 3:
        raise TimeoutError
    print('连接数据库 ... ')
    print('连接数据库成功~~~')

e = Event()
Thread(target=connet_db,args=(e,)).start()
Thread(target=check,args=(e,)).start()

 

from threading import Condition
acquire
release
wait    阻塞
notify  让wait解除阻塞的工具
wait还是notify在执行这两个方法的前后 必须执行acquire和release
from threading import Condition,Thread
def func(con,i):
    con.acquire()
    # 判断某条件
    con.wait()
    print('threading : ',i)
    con.release()

con = Condition()
for i in range(20):
    Thread(target=func,args=(con,i)).start()
con.acquire()
# 帮助wait的子线程处理某个数据直到满足条件
con.notify_all()
con.release()
while True:
    num = int(input('num >>>'))
    con.acquire()
    con.notify(num)
    con.release()

 

from threading import Timer
def func():
    print('执行我啦')

# interval 时间间隔
Timer(0.2,func).start()  # 定时器
# 创建线程的时候,就规定它多久之后去执行

 

  

 

posted @ 2018-07-30 19:49  贾迪123  阅读(120)  评论(0编辑  收藏  举报