win32线程已退出 返回值为0_python多线程threading模块的一些总结

1、threading.Thread

threading.Thread(target=None, name=None, args=(), kwargs={}, *, daemon=None)

target:在线程中调用的对象,可以为函数或者方法;

args,kwargs均为target对象的参数。

daemon:是否设置为守护对象,默认为否。若将一个线程设置为守护线程,则只有守护线程结束python程序才会退出。如果程序在结束时,还有守护线程未进行完,则会强退该进程;而非守护对象只有在自身执行完毕之后才会退出。典型的例子是,主线程末尾开启了一个线程,若为守护线程,开启后会马上在主线程执行结束时被强制退出;若为非守护进程,则会一直等到该进程执行完之后才会退出。

"""
通过实例化threading.Thread类创建线程
"""
import time
import threading


def test_thread(para='hi', sleep=3):
    """线程运行函数"""
    time.sleep(sleep)
    print(para)


def main():
    # 创建线程
    thread_hi = threading.Thread(target=test_thread)
    thread_hello = threading.Thread(target=test_thread, args=('hello', 5), daemon=True)
    # 启动线程
    thread_hi.start()
    thread_hello.start()
    print('Main thread has ended!')


if __name__ == '__main__':
    main()

输出:

(当thread_hello的sleep参数为1)
Main thread has ended!
hello
hi
(当thread_hello的sleep参数为5,在thread_hi执行完之后程序直接结束)
Main thread has ended!
hi

Thread的方法:

start():开启线程,如果线程是通过继承threading.Thread子类的方法定义的,则调用该类中的run()方法;start()只能调用一次,否则报RuntimeError。

join(timeout=None):让当前线程阻塞(一般就是指主线程)等待直到调用join方法的线程结束,timeout参数可以用于设置超时时间。可以使用is_alive()方法来判断线程是否存活(即在run()方法执行的过程中返回True)。join方法可被多次调用,但在线程中调用自己的join方法或者在start()前调用join()会报RuntimeError。

2、threading.lock

使用threading.lock创建线程锁,仅有锁定和非锁定两种状态。锁被创建时,是非锁定状态。

在某一个线程中调用lock.acquire(timeout)方法时,会阻塞其它尝试获取锁的线程(因此在需要确保线程按目标顺序执行时,在这几个线程中调用锁),timeout默认为-1,即直到锁被释放之前始终阻塞其它获取锁的线程,使用其它值则会最多阻塞该时长。

在线程末尾调用lock.release()方法可以释放锁,将其状态改为非锁定,其它尝试获取锁的进程不再阻塞。当然,也可以不在调用锁的进程中释放锁,其它位置均可使用release()方法。在lock非锁定状态下调用release()会导致RuntimeError错误。

"""
使用锁实现线程同步
"""
import time
import threading

# 创建锁
lock = threading.Lock()

# 全局变量
global_resource = [None] * 5


def change_resource(para, sleep):
    # 请求锁
    lock.acquire()

    # 这段代码如果不加锁,第一个线程运行结束后global_resource中是乱的,
# 输出为:修改全局变量为: ['hello', 'hi', 'hi', 'hello', 'hello']
    # 第二个线程运行结束后,global_resource中还是乱的,
# 输出为:修改全局变量为: ['hello', 'hi', 'hi', 'hi', 'hi']
    global global_resource
    for i in range(len(global_resource)):
        global_resource[i] = para
        time.sleep(sleep)
    print("修改全局变量为:", global_resource)

    # 释放锁
    lock.release()


def main():
    thread_hi = threading.Thread(target=change_resource, args=('hi', 2))
    thread_hello = threading.Thread(target=change_resource, args=('hello', 1))
    thread_hi.start()
    thread_hello.start()


if __name__ == '__main__':
    main()

在使用锁时,可以正确依序完成两个线程,输出为:

修改全局变量为: ['hi', 'hi', 'hi', 'hi', 'hi']
修改全局变量为: ['hello', 'hello', 'hello', 'hello', 'hello']

不使用锁,输出为:

修改全局变量为: ['hello', 'hi', 'hi', 'hello', 'hello']
修改全局变量为: ['hello', 'hi', 'hi', 'hi', 'hi']

fc603d50e89582b5c5715107e79dfade.png

在第4秒时thread_hello执行输出,第8秒thread_hi执行输出。结果可见该时间位置global_resources最新的值。

值得注意的一点是,当被锁定的锁在加锁线程中再次acquire()时,该进程也被阻塞,只能在其它未被锁阻塞的线程中release(),否则就成为死锁。

3、threading.Rlock

在锁的基础上加入的所属线程和递归等级的概念。在线程未释放锁之前再次获取锁只会使锁的递归等级+1而不会死锁。只有在先获取锁的进程中释放所有锁(递归等级为0),其它线程才能获取锁。require()方法与lock一样返回上锁成功与否的True/False。

4、threading.Condition

threading.Condition(lock=None)

Condition传入一个锁或递归锁,使得在一个线程中调用其wait()方法时,释放锁并阻塞该线程,直到在其它线程中调用了该condition的notify()或notify_all()方法。此时,被阻塞的线程重新获得锁。

acquire(): 请求锁,若使用递归锁,则请求底层锁;

release(): 释放底层锁

wait(timeout=None): 释放锁并阻塞线程,直到其它线程notify(),notify_all(),并且release()掉condition或者超时。此时,被阻塞的线程重新获得锁。

wait_for(predicate, timeout=None): 与wait相似。首先调用predicate,如果predicate返回的是Ture,则不会释放锁,直接往后执行;若不为True,则释放锁,知道predicate的返回值是True。值得注意的是,wait_for()也要等到其他线程中notify或notify_all通知且释放锁之后,才会再计算predicate的值,若为True,则原进程获得锁继续执行,若为False,继续阻塞直到timeout。即:while not predicate(): condition_lock.wait()。无锁情况调用wait和wait_for都会报RuntimeError。

"""
让一个线程等待,直到另一个线程通知
"""
import time
import threading


# 创建条件变量对象
condition_lock = threading.Condition()

PRE = 0


# predicate可调用函数
def pre():
    print(PRE)
    return PRE


def test_thread_hi():
    # 在使用wait/wait_for之前必须先获得锁
    condition_lock.acquire()

    print('等待线程test_thread_hello的通知')
    # 先执行一次pre,返回False后释放掉锁,等另一个线程释放掉锁后再次执行pre,返回True后再次获取锁
    # wait_for的返回值不是True和False,而是predicate参数的返回值
    condition_lock.wait_for(pre)
    # condition_lock.wait()
    print('继续执行')

    # 不要忘记使用wait/wait_for之后要释放锁
    condition_lock.release()


def test_thread_hello():
    time.sleep(1)
    condition_lock.acquire()

    global PRE
    PRE = 1
    print('修改PRE值为1')

    print('通知线程test_thread_hi可以准备获取锁了')
    condition_lock.notify()
    
    # 先notify/notify_all之后在释放锁
    condition_lock.release()
    print('你获取锁吧')


def main():
    thread_hi = threading.Thread(target=test_thread_hi)
    thread_hello = threading.Thread(target=test_thread_hello)
    thread_hi.start()
    thread_hello.start()


if __name__ == '__main__':
    main()

输出:

等待线程test_thread_hello的通知
0
修改PRE值为1
通知线程test_thread_hi可以准备获取锁了
你获取锁吧
1
继续执行 

### 若使用wait,则输出:
等待线程test_thread_hello的通知
修改PRE值为1
通知线程test_thread_hi可以准备获取锁了
你获取锁吧
继续执行

5、threading.Semaphore

threading.Semphore(value=1)

threading的信号量对象。每调用一次acquire(blocking=True, Timeout=None) value-1,acquire返回True,如果在调用acquire前value已经为0,则调用acquire时阻塞当前线程直到使用release方法唤醒,acquire-1并返回True。blocking为False则Value为0不会阻塞。timeout设置后,时间内未获得信号则直接返回False,否则返回True,和前面一样。调用一次release() value+1,唤醒等待value大于0的线程。

"""
通过信号量对象管理一次性运行的线程数量
"""
import time
import threading

# 创建信号量对象,初始化计数器值为3
semaphore3 = threading.Semaphore(3)


def thread_semaphore(index):
    # 信号量计数器减1
    semaphore3.acquire()
    time.sleep(2)
    print('thread_%s is running...' % index)
    # 信号量计数器加1
    semaphore3.release()


def main():
    # 虽然会有9个线程运行,但是通过信号量控制同时只能有3个线程运行
    # 第4个线程启动时,调用acquire发现计数器为0了,所以就会阻塞等待计数器大于0的时候
    for index in range(9):
        threading.Thread(target=thread_semaphore, args=(index, )).start()


if __name__ == '__main__':
    main()

输出:程序会三个三个执行。

6、threading.Event

用于不同进程间的相互通知,在建立时默认为False。方法如下:

is_set(): 当内部标志为True时返回True

set(): 将内部标志置为True。此时唤醒所有等待中的线程,调用wait()方法的线程将不会被阻塞

clear(): 将内部标志置为False。所有调用wait()方法的线程将被阻塞,直到调用set()方法

wait(): 阻塞当前线程直到内部标志位True或超时。如果调用时内部标志本身就是True,则不会被阻塞

"""
事件对象使用实例
"""
import time
import threading

# 创建事件对象,内部标志默认为False
event = threading.Event()


def student_exam(student_id):
    print('学生%s等监考老师发卷。。。' % student_id)
    event.wait()
    print('开始考试了!')


def invigilate_teacher():
    time.sleep(5)
    print('考试时间到,学生们可以开始考试了!')
    # 设置内部标志为True,并唤醒所有等待的线程
    event.set()


def main():
    for student_id in range(3):
        threading.Thread(target=student_exam, args=(student_id, )).start()

    threading.Thread(target=invigilate_teacher).start()


if __name__ == '__main__':
    main()

输出:

学生0等监考老师发卷。。。
学生1等监考老师发卷。。。
学生2等监考老师发卷。。。
考试时间到,学生们可以开始考试了!
开始考试了!
开始考试了!
开始考试了!

7、threading.Timer

定时器,表明一个操作需要在等待一定时间后执行。

threading.Timer(interval, function, args=None, kwargs=None)

interval:定时器秒数

function:执行的函数

args,kwargs:funciton的参数

方法:

cancel(): 停止计时器,并取消该函数的执行。cancel只在计时器计时结束前生效,如果function已经开始执行,则不会生效。

import threading
import time

def hello(name):
    print ("hello %sn"  % name)
    # global timer
    # timer = threading.Timer(2.0, hello, ["Hawk"])
    # timer.start()

if __name__ == "__main__":
    timer = threading.Timer(2.0, hello, ["Hawk"])   ##每隔两秒调用函数hello
    timer.start()
    time.sleep(3)
    timer.cancel()

输出:

hello Hawk
### 当time.sleep(1)时,被cancel无输出

8、threading.Barrier

栅栏对象,用于一些进程需要彼此等待的情况。每个线程都会尝试调用wait()方法,然后阻塞,直到所有线程都调用了wait()方法一起释放。

threading.Barrier(parties, action=None, timeout=None)

parties: 需要创建栅栏对象的线程数

action: 一个可调用的对象,在所有线程被释放时,在释放前,其中随机一个线程会调用action对象

timeout: wait()的超时时间

方法:

wait(timeout=None): 如果使用了wait()方法中的timeout参数,则此参数优先于栅栏定义时的timeout。返回值为range(parties)中的一个整数,此值每个线程都不同。如果action调用一场、超时均会报BrokenBarrierError。

reset(): 将一个栅栏对象重置为默认的初始态。如果此时有线程在等待释放,则报BrokenBarrierError。

abort(): 使栅栏进入BrokenBarrierError异常。当想要放弃一个线程但又不希望发生死锁时调用这个方法。

parties: 通过栅栏的线程数量

n_waiting:在栅栏中等待的线程数量

broken: 若栅栏破损返回True

"""
栅栏对象使用示例
"""
import time
import threading


def test_action():
    print('所有栅栏线程释放前调用此函数!')


# 创建线程数为3的栅栏对象,当“拦住”3个线程的wait后放行,然后又继续“拦”(如果有的话)
barrier = threading.Barrier(3, test_action)


def barrier_thread(sleep):
    time.sleep(sleep)
    print('barrier thread-%s wait...' % sleep)
    # 阻塞线程,直到阻塞线程数达到栅栏指定数量
    barrier.wait()
    print('barrier thread-%s end!' % sleep)


def main():
    # 这里开启了6个线程,则一次会拦截3个
    for sleep in range(6):
        threading.Thread(target=barrier_thread, args=(sleep, )).start()


if __name__ == '__main__':
    main()

输出:

barrier thread-0 wait...
barrier thread-1 wait...
barrier thread-2 wait...
所有栅栏线程释放前调用此函数!
barrier thread-2 end!
barrier thread-0 end!
barrier thread-1 end!
barrier thread-3 wait...
barrier thread-4 wait...
barrier thread-5 wait...
所有栅栏线程释放前调用此函数!
barrier thread-5 end!
barrier thread-3 end!
barrier thread-4 end!

python中with语法对threading的处理:

with thread_lock:
    func()

等同于:

thread_lock.acquire()
try:
    func()
finally:
    thread_lock.release()

with会在开始获得锁并在func()执行完后释放锁。若在进入with语句前thread_lock是已经被获取的锁,则会持续等待知道在其它线程中lock被释放。


本文很大程度上参考了CNblog作者山上下了雪的文章,并在一些地方增加了样例和注解,原文链接如下:

Python内置库:threading(多线程操作) - 山上下了雪 - 博客园​www.cnblogs.com
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值