Python并发之多线程threading(1)

Threading用于提供线程相关的操作。线程是应用程序中工作的最小单元,它被包含在进程之中,是进程中的实际运作单位。

当不同的线程需要操作共享数据时,当两个或以上对共享内存的操作发生在并发线程中,并且至少有一个可以改变数据,又没有同步机制的条件下,就会产生竞争条件,可能会导致执行无效代码、bug、或异常行为。

需要通过某些机制控制共享数据的读写,确保同一个时刻只能有一个线程能写数据。Threading模块有一系列同步原语控制实现线程共享数据的同步。

threading模块提供的可直接调用函数:

方法名 详细说明
threading.active_count() 返回当前处于激活状态的Thread对象个数,返回的个数等于threading.enumerate()返回的list的长度
threading.current_thread() 返回当前的Thread对象,相当于调用者的线程
threading.get_ident() 返回当前线程的线程identifier,这是一个非零值
threading.enumerate() 返回当前处于激活状态的Thread对象,以list形式返回,包含daemonic线程,dummy线程和main线程
threading.main_thread() 返回main线程对象,一般是Python解释器开始运行的线程
threading.settrace(func) 为所有从threading module创建的线程设置一个trace函数,该函数会在每个线程的run()方法被调用前,被传给sys.settrace()
threading.setprofile(func) 为所有从threading module创建的线程设置一个profile函数,该函数会在每个线程的run()方法被调用前,被传给sys.setprofile()
threading.stack_size([size]) 当创建一个新的线程时返回该线程使用的stack size,size参数可选,用来指定所创建线程的stack size,必须是0或是一个至少为32768的正整数。size未指定会默认使用0值,RuntimeError表示改变stack size不支持,ValueError表示stack size非法值。32K的stack size是目前支持最小的值,足够的stack空间主要是解释器本身需要占用。一般memory是4096 Bytes一个page,Stack size建议设置为4K Bytes的倍数
threading.TIMEOUT_MAX 阻塞函数(Lock.acquire(), RLock.acquire(), Condition.wait()等)的允许最大的超时值,如果使用的timeout参数大于这个最大值,就会发生OverflowError

多线程的同步原语如下:

  • Lock: Lock用于对互斥操作(单一资源,全局变量) 。这个对象可以有两种装填:锁住的(locked)和没锁住的(unlocked)。一个Lock对象有两个方法, acquire() 和 release() ,来控制共享数据的读写权限。
  • Rlock: 递归锁对象,也叫可重入锁。其用途和方法同 Lock类似。可重入锁最大的作用是在需要重复获得锁的情况下(如:递归调用)避免死锁
  • Semaphore: 用来共享资源,例如,支持固定数量的共享连接。
  • Condition: 此对象用来同步部分工作流程,在并行的进程中,有两个基本的方法: wait() 用来等待进程, notify_all() 用来通知所有等待此条件的进程。
  • Event: 实现了进程间的简单通讯,一个进程发事件的信号,另一个进程等待事件的信号。 Event 对象有两个方法, set() 和 clear() ,来管理自己内部的变量。
  • Barrier: 将程序分成几个阶段,适用于有些线程必须在某些特定线程之后执行。处于障碍(Barrier)之后的代码不能同处于障碍之前的代码并行。(Python 3.2及以后版本支持)

同步原语适用范围:

Lock用于对互斥操作(单一资源,全局变量) 
RLock与Lock类似,区别仅在与RLock在同一个线程可以多次获取 
Semaphone/BounderSemaphone用于对多个资源的申请使用, 如果BounderSemaphone(1)则==Lock() 
Condition用于在等待某种事情发生 
Event实际上是对Condition的一种操作简化包装,也更符合事件驱动的概念。 
Barrier类是设置了一个线程数量障碍,当等待的线程到达了这个数量就会唤醒所有的等待线程。

1.多线程的基本用法

Thread类是可以单独控制和运行的线程,可以通过传递一个可调用对象给构造器,或者重写子类的run()方法来指定线程的运作。线程对象被创建,就必须通过start()方法来开始运作,此时会调用其run()方法,线程就激活了。直到run()方法终止,线程就停止。 
其他线程可以调用本线程的join()方法,此时调用本线程join()方法的线程会阻塞,等待本线程执行结束退出,再继续调用线程的后续执行。 
线程也可以标识为daemon线程,在Python程序退出时,daemon线程仍然保留,知道关机时才会退出。 
还有一种main线程,即开始运行Python程序的线程,为非daemon线程。 
还有dummy线程,这种线程是在threading module之外开启,比如通过调用的C代码创建的线程。

类及方法 详细说明
Thread类 class threading.Thread(group=None,target=None,name=None,args=(),kwargs={},*,daemon=None),类构造器,group表示线程组,目前不支持该功能,用于后续扩展。target是可以被run()方法调用的对象。name是线程名称。args是参数tuple,用于被target调用。kwargs是关键字参数dictionary,用于被target调用。daemon表明线程是否为daemon类别。在Thread子类重写构造器时,必须首先调用Thread.init()
start() 开始线程的生命周期,该方法会安排run()方法的调用,如果调用start()多次,会发生RuntimeError
run() 线程的生命周期-activity
join(timeout=None) 等待直到线程终止。如果timeout非零,其值为浮点数,要判断join()是否超时,需要使用is_alive()方法看,如果在调用了join()方法后再调用同一线程的is_alive()方法,如果还是alive的,说明join()超时了。如果timeout为None,则调用者会一直阻塞直到被调用join()方法的线程终止。一个线程的join()可被多次调用
getName() 获取线程名
setName() 设置线程名
ident 线程idnetifier,线程是否已经启动,未启动会返回一个非零整数;
is_alive() 判断线程是否alive,主要是看run()是否结束
daemon daemon线程标志
isDaemon 判断是否daemon线程
setDaemon 设置为daemon线程

用法一:

创建一个threading.Thread对象,在它的初始化函数(__init__)中将可调用对象作为参数传入,然后使用start()方法启动线程。

import threading
import time

def music(data):
    print("bengin listen music: {}".format(time.ctime()))
    time.sleep(1)
    print(str(data))
    print("music end: {}".format(time.ctime()))

def movie(data):
    print("bengin look movie: {}".format(time.ctime()))
    time.sleep(3)
    print(str(data))
    print("movie end: {}".format(time.ctime()))

th1 = threading.Thread(target=music, args=("love.mp3",)) ##创建线程
th1.start()  ##启动线程
th2 = threading.Thread(target=movie, args=("Anit.avi",))
th2.start()

#输出如下:
bengin listen music: Thu Oct 11 21:35:40 2018 
bengin look movie: Thu Oct 11 21:35:40 2018
love.mp3
music end: Thu Oct 11 21:35:41 2018
Anit.avi
movie end: Thu Oct 11 21:35:43 2018

从以上输出可以看到,两个线程各自独立运行之后分别结束。彼此互补干扰。

用法二:

通过继承Thread类,重写它的run方法。

import threading
import time

class MultipleThreading(threading.Thread):

    def __init__(self, func, args=(), kwargs=None):
        threading.Thread.__init__(self)
        self.func = func
        self.args = args
        if kwargs is None:
            kwargs = {}
        self.kwargs = kwargs

    def run(self): ##重写run()方法
        print('func_name is: {}'.format(self.func.__name__))
        return self.func(*self.args, **self.kwargs)

def music(data):
    print("bengin listen music: {}".format(time.ctime()))
    time.sleep(2)
    print(str(data))
    print("music end: {}".format(time.ctime()))

def movie(data):
    print("bengin look movie: {}".format(time.ctime()))
    time.sleep(5)
    print(str(data))
    print("movie end: {}".format(time.ctime()))

th1 = MultipleThreading(music, ("love.mp3",))
th2 = MultipleThreading(movie, ("Anit.avi",))
th1.start()
th2.start()

threading.Thread的其他实例方法:

(1)join()方法

join()方法会阻塞主线程,当启动线程之后,只有线程全部结束,主线程才会继续执行。

没使用join()方法时:

import threading
import time

def music(data):
    print("bengin listen music: {}".format(time.ctime()))
    time.sleep(2)
    print(str(data))
    print("music end: {}".format(time.ctime()))

def movie(data):
    print("bengin look movie: {}".format(time.ctime()))
    time.sleep(5)
    print(str(data))
    print("movie end: {}".format(time.ctime()))

thread_list = []

th1 = threading.Thread(target=music, args=("love.mp3",))
th2 = threading.Thread(target=movie, args=("Anit.avi",))
th1.start()
time.sleep(1)
th2.start()
print("main thread continue: {}".format(time.ctime()))

##输出如下:
bengin listen music: Thu Oct 11 22:26:51 2018
bengin look movie: Thu Oct 11 22:26:52 2018
main thread continue: Thu Oct 11 22:26:52 2018 ##没有使用join方法时,线程执行不会block主线程
love.mp3
music end: Thu Oct 11 22:26:53 2018
Anit.avi
movie end: Thu Oct 11 22:26:57 2018

使用join()方法,线程会阻塞主线程

import threading
import time

def music(data):
    print("bengin listen music: {}".format(time.ctime()))
    time.sleep(2)
    print(str(data))
    print("music end: {}".format(time.ctime()))

def movie(data):
    print("bengin look movie: {}".format(time.ctime()))
    time.sleep(5)
    print(str(data))
    print("movie end: {}".format(time.ctime()))

thread_list = []

th1 = threading.Thread(target=music, args=("love.mp3",))
th2 = threading.Thread(target=movie, args=("Anit.avi",))
thread_list.append(th1)
thread_list.append(th2)

for th in thread_list:
    th.start()
    th.join()

print("main thread continue: {}".format(time.ctime()))

##输出如下:
bengin listen music: Thu Oct 11 22:31:33 2018
love.mp3
music end: Thu Oct 11 22:31:35 2018
bengin look movie: Thu Oct 11 22:31:35 2018
Anit.avi
movie end: Thu Oct 11 22:31:40 2018
main thread continue: Thu Oct 11 22:31:40 2018 ##线程全部结束之后才能继续主线程

(2)setDaemon()方法

使用setDaemon()方法设置线程为守护线程,就是子线程,跟着主线程一起退出。

注意:setDaemon()方法一定在start之前设置,否则会报错。
import threading
import time

def music(data):
    print("bengin listen music: {}".format(time.ctime()))
    time.sleep(2)
    print(str(data))
    print("music end: {}".format(time.ctime()))

def movie(data):
    print("bengin look movie: {}".format(time.ctime()))
    time.sleep(5)
    print(str(data))
    print("movie end: {}".format(time.ctime()))

thread_list = []

th1 = threading.Thread(target=music, args=("love.mp3",))
th2 = threading.Thread(target=movie, args=("Anit.avi",))
thread_list.append(th1)
thread_list.append(th2)

for th in thread_list:
    th.setDaemon(True)
    th.start()

print("main thread continue: {}".format(time.ctime()))

##输出:
bengin listen music: Thu Oct 11 22:36:13 2018 
bengin look movie: Thu Oct 11 22:36:13 2018
main thread continue: Thu Oct 11 22:36:13 2018  ##线程任务还没完成,但是会随着主线程结束而同时结束

2, 多线程Lock/RLock数

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值