Python学习笔记16(多线程)

Threading用于提供线程相关的操作,线程是应用程序中工作的最小单元。

一、使用多线程

线程有两种调用方式、直接调用和继承式调用。

import threading,time

def hello(name):
    print("my name is ",name)
    time.sleep(3)

if __name__ == "__main__":

    t1 = threading.Thread(target=hello,args=("zhangyan",))  #生成一个线程
    t2 = threading.Thread(target=hello,args=("spring",))    #生成另一个线程

    t1.start() #启动t1
    t2.start() #启动t2
直接调用
import threading,time

class MyThread(threading.Thread): #一定要继承Thread类并且重写run方法

    def __init__(self,name):
        threading.Thread.__init__(self) #一定要显示的调用父类的构造函数
        self.name = name

    def run(self): #每个线程要运行的函数
        print("my name is ",self.name)
        time.sleep(3)


if __name__ == "__main__":
    t1 = MyThread("zhangyan")
    t2 = MyThread("spring")
    t1.start()
    t2.start()
继承式调用

构造方法:

Thread(group=None, target=None, name=None, args=(), kwargs={}) 

  group: 线程组,目前还没有实现,库引用中提示必须是None; 
  target: 要执行的方法; 
  name: 线程名; (可以在这里给线程起名字,也可以使用setName()方法设置名字)
  args/kwargs: 要传入方法的参数。

更多方法:

        start:线程准备就绪,等待CPU调度

        setName:为线程设置名称

        getName:获取线程名称

        setDaemon:设置为后台线程或前台线程(默认)

                    如果是后台线程,主线程执行过程中,后台线程也在进行,主线程执行完毕后,后台线程不论成功与否,均停止,

                    如果是前台线程,主线程执行过程中,前台线程也在进行,主线程执行完毕后,等待前台线程也执行完成后,程序停止。

        join:等待子线程运行结束

import threading,time

def hello(name):
    print("start...",name)
    time.sleep(3)
    print("end...",name)

if __name__ == "__main__":

    t1 = threading.Thread(target=hello,args=("zhangyan",))  #生成一个线程
    t2 = threading.Thread(target=hello,args=("spring",))    #生成另一个线程

    t1.setDaemon(False)  #设置一定要设置在start之前,默认就是False,不写也行
    t2.setDaemon(False)

    t1.start() #启动t1
    t2.start() #启动t2

    print("main thread end!") #主线程运行结束


>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
#运行结果
start... zhangyan
start... spring
main thread end!
end... zhangyan
end... spring
setDaemon(False)

多线程设置为前台线程,主线程结束程序并没有停止,而是其他线程也结束后才停止。

setDaemon()一定要设置在start()之前

import threading,time

def hello(name):
    print("start...",name)
    time.sleep(3)
    print("end...",name)

if __name__ == "__main__":

    t1 = threading.Thread(target=hello,args=("zhangyan",))  #生成一个线程
    t2 = threading.Thread(target=hello,args=("spring",))    #生成另一个线程

    t1.setDaemon(True)  #设置为后台线程
    t2.setDaemon(True)

    t1.start() #启动t1
    t2.start() #启动t2

    print("main thread end!") #主线程结束

>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
#运行结果
start... zhangyan
start... spring
main thread end!
setDaemon(True)

多线程设置为后台线程,主线程结束后程序立刻停止,未等到后台线程运行结束。

import threading,time

def hello(name):
    print("start...",name)
    time.sleep(3)
    print("end...",name)

if __name__ == "__main__":

    thread_list = []  #创建线程存储的列表

    for i in range(4):
        t = threading.Thread(target=hello,args=str(i))
        t.setDaemon(True)
        thread_list.append(t)

    for t in thread_list:
        t.start()

    for t in thread_list:
        t.join()

    print("main thread end!")

>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
#运行结果
start... 0
start... 1
start... 2
start... 3
end... 3
end... 2
end... 1
end... 0
main thread end!
join

也就是说,只有在多线程设置为后台线程时候,join才有用武之地。如果多线程设置为前台线程(未设置也是前台线程,默认的),程序本身就会等待全部线程运行结束后才停止,不需要join。

import threading,time

def hello(name):
    print("start...",name)
    time.sleep(3)
    print("end...",name)

if __name__ == "__main__":

    for i in range(4):
        t = threading.Thread(target=hello,args=str(i))
        t.setDaemon(True)
        t.start()
        t.join()

    print("main thread end!")

>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
start... 0
end... 0
start... 1
end... 1
start... 2
end... 2
start... 3
end... 3
main thread end!
join的错误用法

也不能说是为错误用法,只是这个用法使得多线程又变成了单线程,使得多线程变得毫无意义。

创建每个线程的时候被设置了join,下一个线程就会等待这个线程结束之后才创建。

这也是之前的例子中需要使用thread_list来存储线程的原因,因为创建完毕之后还需要遍历一遍设置个join。

二、线程锁(Lock、RLock)

试想一下,多线程操作的时候是否可能会发生如下的情况。

在某一条线程对数据进行+1操作的时候,刚获取到数据还未进行+1(n条指令执行完毕),这时候CPU切换到了另一条线程上,这个线程也同样进行+1的操作。假如说原数据是3,那么这两个线程交给CPU运算的数据都是3,运算结束后变成了4,而不是设想中的5。

import threading,time

gl_num = 0

def show(arg):
    global gl_num
    time.sleep(1)
    gl_num +=1
    print(gl_num)

for i in range(10):
    t = threading.Thread(target=show, args=(i,))
    t.start()

print('main thread stop')
未使用锁

多次运行可能产生混乱,为了多个线程同时操作一个内存中的资源时不产生混乱,我们使用锁。

(python3中好像默认就使用了锁,所以在使用上述的例子的时候不会产生混乱。)

import threading,time

gl_num = 0

def show(arg):
    global gl_num
    time.sleep(1)
    lock.acquire()
    gl_num +=1
    lock.release()
    print(gl_num)

if __name__ == "__main__":

    lock = threading.Lock()

    for i in range(10):
        t = threading.Thread(target=show, args=(i,))
        t.start()

    print('main thread stop')
Lock

RLock中除了状态locked和unlocked外还记录了当前lock的owner和递归层数,使得RLock可以被同一个线程多次acquire()。

三、信号量(Semaphore)

线程锁(互斥锁)同时只允许一个线程更改数据,而Semaphore是同时允许一定数量的线程更改数据 ,比如厕所有3个坑,那最多只允许3个人上厕所,后面的人只能等里面有人出来了才能再进去。

import threading, time


def run(n):
    semaphore.acquire()
    time.sleep(1)
    print("run the thread: %s" % n)
    semaphore.release()


if __name__ == '__main__':

    num = 0
    semaphore = threading.BoundedSemaphore(5)  # 最多允许5个线程同时运行
    for i in range(20):
        t = threading.Thread(target=run, args=(i,))
        t.start()
Semaphore

四、事件(event)

事件主要提供了三个方法 set、wait、clear。

事件处理的机制:全局定义了一个“Flag”,如果“Flag”值为 False,那么当程序执行 event.wait 方法时就会阻塞,如果“Flag”值为True,那么event.wait 方法时便不再阻塞。

  • clear:将“Flag”设置为False
  • set:将“Flag”设置为True
  • isset:当内置标志为True时返回True。

通过Event来实现两个或多个线程间的交互,下面是一个红绿灯的例子,即起动一个线程做交通指挥灯,生成几个线程做车辆,车辆行驶按红灯停,绿灯行的规则。

import threading,time
import random

def light():
    if not event.isSet():
        event.set() #wait就不阻塞 #绿灯状态
    count = 0
    while True:
        if count < 10:
            print('\033[42;1m--green light on---\033[0m')
        elif count <13:
            print('\033[43;1m--yellow light on---\033[0m')
        elif count <20:
            if event.isSet():
                event.clear()
            print('\033[41;1m--red light on---\033[0m')
        else:
            count = 0
            event.set() #打开绿灯
        time.sleep(1)
        count +=1

def car(n):
    while 1:
        time.sleep(random.randrange(10))
        if  event.isSet(): #绿灯
            print("car [%s] is running.." % n)
        else:
            print("car [%s] is waiting for the red light.." %n)

if __name__ == '__main__':
    event = threading.Event()
    Light = threading.Thread(target=light)
    Light.start()
    for i in range(3):
        t = threading.Thread(target=car,args=(i,))
        t.start()
event

 五、Timer

Timer(定时器)是Thread的派生类,用于在指定时间后调用一个方法。

Timer(interval, function, args=[], kwargs={}) 
interval: 指定的时间 
function: 要执行的方法 
args/kwargs: 方法的参数

import threading
 
def func():
    print 'hello timer!'
 
timer = threading.Timer(5, func)
timer.start()
Timer

 

posted @ 2017-08-28 14:18  爱锁屏  阅读(187)  评论(0编辑  收藏  举报