python学习20

多线程

基本概念:
          线程:进程中的每个子任务,不能独立存在,CPU执行的最小单位
          进程:独立的所有子任务的集合
          线程,进程:目的都是想同时完成任务
特点:
进程的特点:独立(内存独立,cpu使用独立)启动进程开销大(速率低),进程之间很难共享数据,和数据通信数据安全高

线程的特点:依赖进程(内存共享,CPU使用独立)启动开销小线程之间共享数据容易方便通信线程不安全

我们使用threading模块来运用线程

threading.currentThread(): 返回当前的线程变量。
threading.enumerate(): 返回一个包含正在运行的线程的list。正在运行指线程启动后、结束前,不包括启动前和终止后的线程。

threading.activeCount(): 返回正在运行的线程数量,与len(threading.enumerate())有相同的结果。

举个列子:

import threading

def main():
    print(threading.active_count())   #线程有几个
    print(threading.enumerate())#这几个线程是哪几个
    mm=threading.current_thread()   #获取当前正在执行的线程
    mm.setName('sssss')         #给这个线程重命名为sssss
    print(threading.current_thread())    #现在执行的线程是哪个

if __name__=='__main__':
    main()

得到如下结果:

1
[<_MainThread(MainThread, started 4796)>]
<_MainThread(sssss, started 4796)>
得到的数据为,当前运行的线程只有一个,是这几个,名字为sssss。

除了使用方法外,线程模块同样提供了 Thread类来处理线程,Thread类提供了以下方法:
run(): 用以执行线程活动的方法。一般不使用。

start():启动线程活动使线程达到一个就绪状态。

run()和start()个人理解的区别在于,使用run()时,正在执行的对象会暂停,先执行run()的对象,执行完之后继续执行原来的对象。而使用start(),会是start对象进入执行状态,然后开始和正在执行的线程以多线程的方式运行。

join([time]):强制调用某个线程进入执行状态,本线程礼让CPU资源,进入阻塞,休眠状态,直至某个线程退出/抛异常/超时 本线程才进入就绪状态  等待至线程中止。这阻塞调用线程直至线程的join() 方法被调用中止-正常退出或者抛出未处理的异常-或者是可选的超时发生。
isAlive(): 返回线程是否活动的。
getName(): 返回线程名。

setName(): 设置线程名。

我们来举个简单的例子:

import threading,time
def T1_job():
    print('T1 start\n')
    for i in range(10):
        time.sleep(0.1)
    print('T1 finish\n')

def T2_job():
    print('T2 start\n')
    print('T2 finish\n')

def main():     
    T1_thread=threading.Thread(target=T1_job,name='T1')    #创建一个线程  target是目标
    T2_thread=threading.Thread(target=T2_job,name='T2')

    T1_thread.start()
    T2_thread.start()
    T1_thread.join()   #join()  执行完上面的thread之后才会执行下面的程序
    print('all done\n')

if __name__=='__main__':
    main()
上面这段代码因为是多线程的原因,所有结果不定, 但是由于我在print之前设定了T1_thread.join(),所以会最后执行print。

为了更好理解,我们接着看另一段代码:

#usr/bin/python
#-*-coding:utf-8-*-
import threading
import time

class Mythread(threading.Thread):     #我们定义一个叫Mythread的类,注意这里的参数必须继承threading.Thread

    def run(self):    #重写父类run方法
        for i in range(10):
            print(self.getName(),i)
            time.sleep(1)

if __name__=='__main__':
    t1= Mythread()
    t2=Mythread()
    t1.start()
    t2.start()
    for i in range(10):
        print(threading.current_thread().getName(), i)
        time.sleep(1)
        if i==5:
            t1.join()

这段代码与上段代码有个区别,就是这段代码是以类的方式写线程而上端则以函数的方式

上述这段代码中,一共3个线程交替共同运行,分别为,t1,t2,和运行的主线程for循环,但是在for循环中,我们添加了一个当i等于5时,t1.join(),这代表当运行到i=5时,for线程会停止运行,当t1,t2,运行完成之后才会继续运行for线程。输出结果类似:

Thread-1 0
Thread-2 0
MainThread 0
MainThread 1
Thread-1 1
Thread-2 1
MainThread 2
Thread-2 2
Thread-1 2
MainThread 3
Thread-2 3
Thread-1 3
MainThread 4
Thread-2 4
Thread-1 4
MainThread 5
Thread-2 5
Thread-1 5
Thread-1 6
Thread-2 6
Thread-2 7
Thread-1 7
Thread-1 8
Thread-2 8
Thread-1 9
Thread-2 9
MainThread 6
MainThread 7
MainThread 8
MainThread 9
我们也可以在t1.join()中添加参数时间,代表优先执行多长时间,当时间过后,for会继续和t1,t2多线程运行。

上面说过多线程可能有不安全的风险,为了解决这个问题我们引用了一个

锁当然有锁定和未锁定两种状态,当一个线程要访问共享数据时,必须要先获得锁定,如果已经有别的线程获得锁定,那么就进入暂停状态,等别的线程把锁释放后,再进行操作。

Condition:更精确的控制锁,提供了四个方法,上锁(acquire()),等待(wait()),解锁(release()),唤醒(notify(),notify_all())

我们一般更多的会使用Condition。举个例子:

我们假设3个对象同时开始强火车票,并返回谁抢到第几张,还剩多少张:

import threading
numbers=50
count1=0
lock=threading.Lock()  #创建锁
cond=threading.Condition(lock=lock)    #管理锁
class qiangpiao(threading.Thread):
    def __init__(self,name):
        threading.Thread.__init__(self)
        self.setName(name)

    def run(self):
        global numbers,count1
        while  True:
            cond.acquire()  #锁定
            if numbers==0:
                return
            numbers-=1
            count1+=1
            print('{0}抢到了{1}票,还剩{2}'.format(self.getName(),count1,numbers))
            time.sleep(0.1)
            cond.release()  #解锁

if __name__=='__main__':
    huangniu=qiangpiao('黄牛')
    mimei = qiangpiao('迷妹')
    zz = qiangpiao('智障')
    huangniu.start()      #注意,创建完一个线程之后必须start
    mimei.start()
    zz.start()

在上述这段代码中,我们首先创建了一个锁lock,接着我们使用Condition管理了这个锁并命名为cond。接着我们创建了一个线程类,先初始化类,并给这个线程获得一个实参名字,接着重新定义了线程的run方法,首先引入两个全局变量numbers和count1,numbers代表的是剩余几张票,count1代表的票的张数。接下来判断numbers是否为0,是则退出程序,不是则返回我们需要的文字,即A抢到了B张票,还剩C张。

下面我们写一个小游戏:假设有一个伙夫在蒸包子,每蒸够10个就呼唤3个食客吃东西,这时伙夫休息,当三个食客将包子吃完会呼唤伙夫继续蒸包子,食客休息。代码如下:

#usr/bin/python
#-*-coding:utf-8-*-
import threading,time

zhenglong=[]   #蒸笼
#创建两把锁,蒸笼锁由伙夫掌握,另一把锁由食客掌握

cook=threading.Lock()
cook_cond=threading.Condition(lock=cook)
eat=threading.Lock()
eat_cond=threading.Condition(lock=eat)

class Cook(threading.Thread):
    def __init__(self):
        threading.Thread.__init__(self)

    def run(self):
        while True:
            global zhenglong
            eat_cond.acquire()      #加锁
            if len(zhenglong)==0:
                print('伙夫开始蒸包子')
                for i in range(1,11):
                    zhenglong.append(i)
                    print('伙夫开始蒸',i,'个包子')
                    time.sleep(0.3)
                eat_cond.notify_all()   #唤醒所有食客
                print('厨师蒸完包子.开始呼唤吃货们醒来吃包子,厨师休眠.')
            eat_cond.release()    #食客锁解锁
            cook_cond.acquire()   #蒸笼加锁
            cook_cond.wait()      #蒸笼等待
            cook_cond.release()   #蒸笼解锁

class eat(threading.Thread):
    def __init__(self,name):
        threading.Thread.__init__(self)
        self.getName=name

    def run(self):
        while True:
            eat_cond.acquire()    #食客锁加锁
            global zhenglong
            if len(zhenglong)==0:
                cook_cond.acquire()   #蒸笼锁加锁
                cook_cond.notify()    #蒸笼锁唤醒
                cook_cond.release()   #蒸笼锁解锁
                eat_cond.wait()       #食客锁等待

            else:
                foot=zhenglong.pop()
                if foot is not None:
                    print('{0}吃了第{1}个包子,还剩{2}个包子'.format(self.getName,foot,len(zhenglong)))
                time.sleep(0.3)
            eat_cond.release()    #食客锁解锁


if __name__=='__main__':
    shazi=eat('傻子')
    quezi=eat('瘸子')
    xiazi=eat('瞎子')
    cook_1=Cook()
    shazi.start()
    quezi.start()
    xiazi.start()
    cook_1.start()

    input()  #形成一个无限循环

首先引用threadingtime库,然后创建了两把锁,分别管理蒸笼食客管理蒸笼的锁由伙夫掌握并建立了一个空列表储存包子的蒸笼。接着创建了两个线程,一个为伙夫蒸包子的线程类Cook,一个为食客吃包子的线程类eat

在Cook中,难点在于修改之后的run中,在run中我们首先创建了一个无限while循环,引入全局变量zhenglong,接着创建了一个for循环用于伙夫蒸包子,注意的是,在伙夫蒸完包子后需唤醒所有的食客eat_cond.notify_all(),所以我们在for循环前后添加了食客锁的上锁和解锁,(Condition的锁的操作,前后必须有该锁的上锁和解锁),接着我们需要使伙夫等待,使用伙夫锁的wait,前后一样加伙夫锁的上锁和解锁。

在eat中,我们首先重新定义了参数,是name值为输入的值,接着重新run()方法,同样创建了一个一个while循环,然后引入全局变量zhenglong当有包子时,我们定义foot为包子的个数(pop()会返回当前删除的个数),接着返回所需的内容;重点是当包子为零时,我们需要唤醒伙夫,并使食客等待,所以在if中写入Cook唤醒与eat等待,同样的在Cook唤醒前后写上加锁与解锁,而伙夫锁必须在整个while循环判断,所以我们在while循环的开始和结尾写上伙夫锁的加锁与解锁。

接着运行这个程序,完成。







猜你喜欢

转载自blog.csdn.net/gy_334/article/details/80518991
今日推荐