python中的线程

一些简单的进程和线程概念

单进程和单线程

程序在运行时可能有多个进程,每个进程中可能有多个线程,单cpu一次只能处理一个进程,采用分片处理让人们感觉是同时在运行。

多进程和多线程

一个物理cup可以分几核,也就可以理解为可以同时处理多个线程, 而且一个线程中的进程可以被不同的cpu处理(这种情况在python中不可能出现这种情况,因为python中有JIL,全局解释器锁,防止多个线程同时被执行,一次只能执行一个线程,后来python创始人使用多进程处理这个问题)

  • 多进程之间资源共享的问题
    一个线程的多个进程共享进程的资源,一个程序的多个线程被不同cpu执行时需要3份相同的程序资源,会造成资源的浪费。

多进程和多线程之间的选择

如果是计算密集型,计算大量的数据,计算数据是使用cpu,所以要使用多进程;如果是IO密集型,IO操作不用一直占用cpu,所以使用多线程较好。IO和计算如果都多则多进程和多线程较好。

线程安全的问题

线程共享内存(共享主线程的内存空间,因为如果不贡献,内存空间的占用会成指数增长,内存消耗太大),进程不共享内存,因为每个进程之间是的内存空间完全独立(单进程)。所以当10个线程要修改同一份数据的时候,有可能打断另一个进程的数据的操作。这里就涉及到线程锁。

#!/usr/bin/env python
#coding:utf-8
import threading
import time
import random
num = 0
def Sum():
    global  num
    time.sleep(2)
    num += 1
    print num 
for i in range(100):
    feitian = threading.Thread(target=Sum)
    feitian.start()
#输出结果:应该是1-100,但是他并没有加到100,是因为,当两个进程同时渠道num这个资源时,同时对num进行加一。所以我们需要加上线程锁,当有线程对资源进行操作时,资源就不能被其他资源获取。

改进程序:
#!/usr/bin/env python
#coding:utf-8
import threading
import time
import random
num = 0
def Sum():
    global  num
    time.sleep(2)
    lock.acquire()
    num += 1
    print num 
    lock.release()
lock = threading.Lock()
for i in range(100):
    feitian = threading.Thread(target=Sum)
    feitian.start()
#注意:这里又可以引出cpu最底层也是单线程的,同一时刻只能有一个线程在执行,在进行分片,只是你感觉不到,分片默认100条cpu指令。也就是说上面的程序在我释放锁之后,不是立即抢占cpu资源,而是等待他执行100条cpu指令之后才开始。但是如果有sleep的时候,因为sleep不占cpu资源,所以就可能直接切。

#这里还要注意当你连续使用两次lock.acquire()的时候,你会发现你的程序阻塞了,因为当他获得一把锁之后(独占cpu),当另一条语句获取锁的时候,cpu会认为是两一个线程获取锁,等待锁的释放,相当于自己等待自己释放,自己又不会释放,所以就阻塞了。如下核心程序:
    lock.acquire()
    num += 1
    lock.acquire()
    num1 += 2
    print num 
    lock.release()
    lock.release()
如果你有这种需求怎么办?
这里就有lock = threading.RLock()还有另外一种比较重要,在下面。

重要

mysql的最大连接数也是相同的原理
lock = threading.BoundedSemaphore(4)
他就是最多同时有几个人获取资源,但是可能造成线程安全

#!/usr/bin/env python
#coding:utf-8
import threading
import time
import random
num = 0
num1 =0
def Sum():
    global  num
    time.sleep(2)
    lock.acquire()
    num += 1
    print num
    lock.release()
lock = threading.BoundedSemaphore(4)
for i in range(100):
    feitian = threading.Thread(target=Sum)
    feitian.start()
#你会返现屏幕输出已经乱码,因为,多个线程同时在抢tty。不懂的伙伴可以对比一下前面的进程锁的程序。

python中事件触发

#!/usr/bin/env python
#coding:utf-8
import threading
import time
from logging import thread
def  Producer():
    print  u"等人来买包子"
    event.wait()
    #在这里阻塞,直到遇到event.set()
    event.clear()
    print u'sb is coming for baozi'
    print "chef:make a baozi"
    time.sleep(5)
    print '你的包子好了'
    event.set()
def Consumer():
    print u"去买包子"
    event.set()
    time.sleep(2)
    print "waiting for ba to be  eat"
    event.wait()
    print u'thank'
event = threading.Event()
p = threading.Thread(target=Producer)
c = threading.Thread(target=Consumer)
p.start()
c.start()
#输出结果:
等人来买包子
去买包子
sb is coming for baozi
chef:make a baozi
waiting for ba to be  eat
还尼玛么好
还尼玛么好
还尼玛么好
你的包子好了
thank

稍微改进一下就相当于select模型

#!/usr/bin/env python
#coding:utf-8
import threading
import time
def  Producer():
    print  u"等人来买包子"
    event.wait()
    event.clear()
    print u'sb is coming for baozi'
    print "chef:make a baozi"
    time.sleep(5)
    print '你的包子好了'
    event.set()
def Consumer():
    print u"去买包子"
    event.set()
    time.sleep(2)
    print "waiting for ba to be  eat"
    while True:
        if event.is_set(): # 相当于异步select,一直在这里检测;
            print u'thank'
            break
        else:
            print "还尼玛么好"
            time.sleep(1)
            #这个sleep相当于在做自己的事情
event = threading.Event()
p = threading.Thread(target=Producer)
c = threading.Thread(target=Consumer)
p.start()
c.start()

python中提供的多线程模块

threading模块

  • start()
    立即执行程序的线程
  • getName()
    获得线程的名字
  • setName()
    设置线程的名字
  • IsDaemon()
    是否是守护线程看,也就是是否等待子线成执行完毕在退出(默认为false,等子进程执行完毕在退出)
  • setDaemon()
    设置子进程是否为守护线程,(在线程开始执行之前设置否则报错,子线在主线程结束后自动销毁)
  • join()
    主线程执行到join()之后等待子线成执行,主线程停止执行,等待子线成执行完毕看,然后主线程在继续执行(有一个参数就是等待时间)
  • run()
    执行指定的函数

    线程并不是越多越好,到达一定程度,他会成为程序的累赘,因为时间片的关系,进程之间的切换要保存上下文,切换上下文。

    练习

  • 实例1
#!/usr/bin/env python
#coding:utf-8
from threading import Thread
def Foo(arg):
    print arg  
print 'before'
#上面是程序在执行的时候创建一个主线程,下面是程序在执行的时候进行的子线程
t1 = Thread(target=Foo,args=('dd',))
#指定子线成执行的函数和参数
t1.setDaemon(True)
t1.start()
t1.join()
#让他执行他不一定马上执行,不一定能立刻得到cpu
print t1.isDaemon()
print 'after'
#输出结果:
before
ddTrue
after
  • 实例2
#!/usr/bin/env python
#coding:utf-8
import time
from threading import Thread
class MyThread(Thread):
    def run(self):
        time.sleep(10)
        print '我是线程'
def Bar():
    print 'bar'
t1 = MyThread(target='bar',args=())
#执行继承Thread过来的构造函数
t1.start()
print 'over'
#输出结果
over
我是线程
  • 实例3
#!/usr/bin/env python
#coding:utf-8
import time
from threading import Thread
class MyThread(Thread):
    def run(self):
        print 'MyThred'
        Thread.run(self)
        #调用了父类的run方法
def Bar():
    print 'bar'
t1 = MyThread(target=Bar)
t1.start()
输出结果:
MyThred
bar

这里写图片描述

  • 实例四(生产者消费者模型)
    这个非常基础,像hello world一样。
#作用
- 解耦 让各个模块之间的关联性降到最低
- 支持并发
- 支持忙闲不均
#!/usr/bin/env python
#coding:utf-8
from threading import Thread
from Queue import Queue
import time
#队列:先进先出和堆栈不同的是:堆栈是后进后出

class Producer(Thread):
    def __init__(self,name,queue):
        '''
        @param name:生产者的名称
        @param queue:容器 
        '''
        self.__Name = name
        self.__Queue = queue
        super(Producer,self).__init__()
    def run(self):
        while True:
            if self.__Queue.full():
                time.sleep(1)
            else:
                self.__Queue.put('包子')
                time.sleep(1)
                print '%s 生产了一个包子' %(self.__Name,)
        #Thread.run(self)
class  Consumer(Thread):
    def __init__(self,name,queue):
        '''
        @param name:生产者的名称
        @param queue:容器 
        '''
        self.__Name = name
        self.__Queue = queue
        super(Consumer,self).__init__()
    def run(self):
        while True:
            if self.__Queue.empty():
                time.sleep(1)
            else:
                self.__Queue.get()
                time.sleep(1)
                print '%s 消费了一个包子' %(self.__Name,)
        #Thread.run(self)
#有关线程安全(同时对同一个文件进行操作,该怎样选择),下面创建一个存放商品的仓库,他就定义了优先级,线程是安全的
que = Queue(maxsize=100)
feitian1 = Producer('飞天1',que)
feitian1.start()
feitian2 = Producer('飞天2',que)
feitian2.start()
feitian3 = Producer('飞天3',que)
feitian3.start()

for item in  range(20):
    name = 'love%d' %(item,);
    temp = Consumer(name,que)
    temp.start()
'''
que.put('1')
que.put('2')
print que.qsize()
print que.get()
print que.get()
'''   

下面有一个更好的程序来理解生产者和消费者的模型,希望对大家有帮助

#!/usr/bin/env python
#coding:utf-8
import threading
import Queue
import time
import random

def Produce():
    while True:
        q.put("包子")
        print "生产了一个包子"
        time.sleep(random.randrange(5))
def Cousume():
    while True:
        try:
            q.get_nowait()
            print "吃了一个包子"
        except Exception:
            print "等着吃包子"
        time.sleep(random.randrange(3))

q = Queue.Queue()
produce1 = threading.Thread(target=Produce)
produce2 = threading.Thread(target= Produce)
produce1.start()
produce2.start()
laogou = threading.Thread(target=Cousume)
hanlong = threading.Thread(target=Cousume)
laogou.start()
hanlong.start()

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值