python中的线程(四)

线程

1.条件

条件相当于一个复杂的锁。它有四个方法  acquire()  release()   wait()   notify()    notify() 这个方法第一次见,它的作用可以理解为造多个锁.      Con = Condition 实例一个锁

我们举个例子来说明情况:

from threading import Thread,Condition
def func(con,i):
    con.acquire()
    con.wait() # 等钥匙
    print('在第%s个循环里'%i)
    con.release()    #这里的归还相当于销毁
con = Condition()
for i in range(10):
    Thread(target=func,args = (con,i)).start()
while True:
    num = int(input('>>>'))
    con.acquire()
    con.notify(num)  # 造钥匙
    con.release()

  运行结果:

解释:

输入3把锁,执行3次func。然后又输入2把,执行2次func 直至把起的10个进程都运行完。

需要注意的是,在代码里写到了con.acquire()  con.release()这里明明归还了钥匙为什么下2个线程不能执行。我们可以把造的钥匙理解成一次性的,用了之后就销毁了。

2.定时器

Timer   就是一种可以把线程定时执行的方法

我们举个简单的例子来说明

# 定时器的讲解
def check():
    print('时间校准')

while True:
    t = Timer(5,  check).start()
    time.sleep(5)   

解释: 

想要做的功能就是 每5秒就去启动一个线程执行时间同步操作

如果少调time等待那五秒造成的结果就是,在很短的时间里(假如0.01秒)会启动100多个线程 他们同时等5秒后,就一起去执行时间校准。这样和我们当初设想的效果是不一样的。

3.队列

咱们之前讲到 进程之间的内存是封闭的,而线程之间的数据是共享的  

那为什么我们要在线程之间用队列呢,可以用列表么?

答案:可以用列表,但是不安全。

  就涉及上修改值的问题,假如a从列表里取出4然后想把它变成五,变成5后存进去,这时候时间片用完了。然后c也取了那个4进行变3操作,取出后变3,很不巧这时候时间片也用完了,轮到了a,a把5放进去,然后c把3放进去。最后的结果是a的数据错误。这样就造成了数据不安全的隐患。而使用queue 其内置的锁就可以帮我们保证数据的安全。

我们这里就举一个特别简单的例子,来介绍如何使用线程里的队列(其实和进程里的差不多)

import queue

q = queue.Queue()  # 队列 先进先出
q.put(2)    # q.put_nowait()       q.get_nowait()
print(q.get())

线程的队列相比于进程的队列有些不一样。首先引用模块不一样,其次线程队列还有其他的形式。如栈队还有优先级队列,下面分别展示一下。

q = queue.LifoQueue()  # 栈 先进后出
q.put(1)
q.put(2)
q.put(3)
print(q.get())

 得到的结果是:3 按照标准队列的话,应该是先进先出。但是栈队的话,就是先进后出。

优先级队列:

q = queue.PriorityQueue()  # 优先级队列
q.put((20,'a'))
q.put((10,'b'))
print(q.get()[1])

   得到的结果是b  因为前面的数字是优先级,越小优先级越高。同时在村的时候以元组存进去的,所以取出的时候加个[1],就能得到我们想要的元素。

4.线程池

终于写到线程池了,走到这里不容易啊。线程池的内容和进程池差不多,只是他们引用的不是同一个模块。

线程池引用的这个:from concurrent.futures import ThreadPoolExecutor
# # 线程池
 def func(i):
     time.sleep(2)
     print('这是线程%s'%i)
     return i
        #  ProcessPoolExecutor 这就是进程池了 其他的不用变
 tpool = ThreadPoolExecutor(max_workers=5)   ThreadPoolExecutor  这是一个线程池
 t_list = []
 for i in range(10):
     t = tpool.submit(func,i)   # 这是一个线程
     t_list.append(t)        # 不知道大家发现了么  在多进程和多线程的时候 就不用start()
 tpool.shutdown()            # 并且也不用写target=func了,这里代替            了以前的     
 t.close()   t.join()
 print('这是主线程')
 for t in t_list:print('******',t.result()) 
 #  这里获得的结果就是 return的结果
 # 当不用shutdown时,它得到的结果值是每个线程池数量的 也就是算出几个值 就立刻返回几个值

运行结果:

解释:

停2秒,然后输出5个线程,然后又输出另外5个线程 最后那个

t.result()结果,它是实时的。不是输出完前面10进程后,再执行下面的代码,而是算出结果了,就返回给result。之所以最后显示,是因为增加了join()

在这里补充一个map方法

def func(i):
    time.sleep(2)
    print('这是线程%s'%i)
    return i*i
tpool = ThreadPoolExecutor(max_workers=5)   # ThreadPoolExecutor  这是一个线程池
tpool.map(func,range(1,11))  # 这里代替了原来的 for循环 可以直接进行多个线程的开启

5.线程池的回调 

例子:

# # 线程池的回调函数
 def func(i):
     time.sleep(2)   # 这里的睡两秒是为了更好理解线程池的作用
     print('这是线程%s'%i)
     return i*i

 def fun2(m):
     print('回调函数得到的值%s'%m.result())

 tpool = ThreadPoolExecutor(max_workers=5)
 for i in range(10):
     tpool.submit(func, i).add_done_callback(fun2)

这里重点题一下有几个常用的方法:

m.result()  回调函数得到的是对象,想要得到结果要 用result()方法

submit(func, i).add_done_callback(fun2)   像之前进程池一样不用写target args

 而且调用回调函数只需要在后面.方法  这天func1 和func2都被执行了

 done()    判断某一个线程是否完成
cancle()    取消某个任务

关于线程的学习就到这里了,之所以写下来。一是因为怕自己忘了,二是因为写博客能够锻炼自己的总结能力。希望自己能继续下去,不要三分钟热度,加油。

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值