python threading 线程

1、创建线程 方法 一

import time
import threading

def countdown(n):    # 先定义一个给线程调用的函数——倒计时函数。
    print('子线程名字:{}'.format(threading.current_thread().name))   # 获取当前线程对象的名字。
    while n > 0:
        print('T-minus:{}'.format(n))
        n -= 1
        time.sleep(1)
        
t = threading.Thread(target=countdown, args=(6,), name='线程名字-1', daemon=False)
		# 实例化一个线程。
		# 参数daemon 默认为False。如果为True, 则主线程结束后,子线程也自动结束。
		# 如果子线程用了join()方法,那么daemon这个参数对线程来说,基本上没什么用了,
		# 因为主线程会一直阻塞,等待子线程先运行结束。
		
t.start()      # 启动线程。
# t.join()     # 利用jion()使主线程阻塞,等待子线程完成后,才能继续运行下面的代码。
time.sleep(2)    # 让主线程先暂停两秒,先让子线程执行两秒。
print('开始主线程代码……')
print('子线程是否在运行:{}'.format(t.is_alive()))  # t.is_alive() 检查子线程的状态,返回True或False。
print('子线程名字:{}'.format(t.name))             # t.name 获取对象为t线程的名字。
print('当前活动线程的个数:{}'.format(threading.active_count()))  # 获取当前活动线程的个数: threading.active_count()
print('主—当前线程的名字:{}'.format(threading.current_thread()))   # 获取当前线程对象。


2 创建线程 方法 二

from threading import Thread
import time

# 继承Thread类来新建一个线程。
class CountdownThread(Thread):
    def __init__(self, n):
        super().__init__()    # 继承父类的初始化方法。
        self.n = n
        
    def run(self):      # 改写run方法。 因为start方法会调用run方法。
        while self.n > 0:
            print('T-minus', self.n)
            self.n -= 1
            time.sleep(1)


c = CountdownThread(5)
c.start()

3、 创建线程 方法 三

这个方法也是一个可以在主线程中,终止子线程运行的办法。

因为一般子线程运行后,是无法在主线程中去终止它的,很可能会出现无法预料的错误。

from threading import Thread
import time

class CountdownTask:    # 定义一个可以终止线程的类。
    def __init__(self):
        self._running = True

    def terminate(self):  
        self._running = False    # 改变 self._running 为False 来终止子线程。
        
    def run(self, n):
        while self._running and n > 0:
            print('T-minus', n)
            n -= 1
            time.sleep(1)

c = CountdownTask()  
t = Thread(target=c.run, args=(10,))
t.start()
time.sleep(3)
c.terminate()     # 这样在主线程中,就能 用 terminate 方法来终止线程了。
t.join()     

4、线程加锁方法 一

import time, threading

# 假定这是你的银行存款:
balance = 0
lock = threading.Lock()    # 先生成一个锁的对象。

def change_it(n):
    # 先存后取,结果应该为0:
    global balance
    balance = balance + n
    balance = balance - n

def run_thread(n):
    for i in range(100000):
        lock.acquire()  	# 在运行 change_it(n) 前,先要获取锁。
        try:
            change_it(n)
        finally:      	    # 改完了一定要释放锁:
            lock.release()


t1 = threading.Thread(target=run_thread, args=(5,))
t2 = threading.Thread(target=run_thread, args=(8,))
t1.start()
t2.start()
t1.join()
t2.join()
print(balance)

5、线程加锁方法 二

利用上下文管理器 with

import time, threading

# 假定这是你的银行存款:
balance = 0
lock = threading.Lock()

def change_it(n):
    # 先存后取,结果应该为0:
    global balance
    balance = balance + n
    balance = balance - n

def run_thread(n):
    for i in range(100000):
        with lock:       # 会自动获取锁lock.acquire(),退出时释放锁 lock.release()。
            change_it(n)
            
t1 = threading.Thread(target=run_thread, args=(5,))
t2 = threading.Thread(target=run_thread, args=(8,))
t1.start()
t2.start()
t1.join()
t2.join()
print(balance)

6、变量传递方法 一

用 线程安全的 字典 来传递,来分隔每个线程 同个名称 不同的变量值。

import threading


global_dict = {}     # 定义一个全局变量的字典。


def process_stedunt():
    std_name = global_dict[threading.current_thread()]
    print('学生:{:3s}, 在 线程 {} 中。'.format(std_name, threading.current_thread()))


def process_thread(name):
    global_dict[threading.current_thread()] = name     # 通过字典的 键值对来传递,
    process_stedunt()							    	# 这样每个线程的变量就不会互相干涉了。


t1 = threading.Thread(target=process_thread, args=('李雷', ))    # 线程 1,name 变量的值为‘李雷’
t2 = threading.Thread(target=process_thread, args=('韩梅梅', ))   # 线程 2,name 变量的值为‘韩梅梅’
t1.start()
t2.start()

执行结果为:

学生:李雷 , 在 线程 <Thread(Thread-1, started 4688)> 中。
学生:韩梅梅, 在 线程 <Thread(Thread-2, started 13820)> 中。

7、变量传递方法 二 (推荐方法)

利用 threading 中的 local 类来传递

import threading


local_school = threading.local()    # 利用 local() 类,先实例化一个对象。

def process_stedunt():
    std_name = local_school.student    # 读取 local_school.student 的属性值。
    print('学生:{:3s}, 在 线程 {} 中。'.format(std_name, threading.current_thread()))


def process_thread(name):
    local_school.student = name    # 相当于给实例 local_school 一个 name 的属性,并赋值。
    process_stedunt()


t1 = threading.Thread(target=process_thread, args=('李雷', ))
t2 = threading.Thread(target=process_thread, args=('韩梅梅', ))
t1.start()
t2.start()

8、利用队列 queue 来进行线程间的通信

import threading
import queue
import time

def wash(dish_list, dish_queue):
    for dish in dish_list:
        print('{}:开始洗"{}"号盘子。\n'.format(threading.current_thread().name, dish))
        time.sleep(1)
        dish_queue.put(dish)
        #dish_queue.task_done()

def dry(dish_queue):
    while True:
        dish = dish_queue.get()    # 利用get()遍历队列中的元素。
        print('{}:正在烘干"{}"号盘子。\n'.format(threading.current_thread().name, dish))
        time.sleep(3)
        dish_queue.task_done()    # 要加上task_done()告诉队列:get()出来的元素处理完毕了。
                                  # 否则后面的 dish_queue.join() 不知道是否处理完毕,就会一直阻塞主线程。


dish_queue = queue.Queue()
dish_list1 = list(range(5))
dish_list2 = list(range(6,15))

dry_thread1 = threading.Thread(target=dry, args=(dish_queue,), name='线程-dry1')
dry_thread2 = threading.Thread(target=dry, args=(dish_queue,), name='线程-dry2')
dry_thread1.start()
dry_thread2.start()

wash_thread = threading.Thread(target=wash, args=(dish_list2, dish_queue), name='线程-wash')
wash_thread.start()   

wash(dish_list1, dish_queue)     # 留个思考题,这函数如果不是在主线程中调用,也让它在子线程中调用,那么下面的 join()又是怎样一个情况呢??? 

dish_queue.join()    # join()阻塞主线程,等子线程全部遍历了队列中的元素,才继续执行主线程的代码。
                    
print('主线程结束。')

9 threading.Event

threading.Event 可以使一个线程等待其他线程的通知,我们把这个Event传递到线程对象中,Event默认内置了一个标志,初始值为False。一旦该线程通过 wait(timeout) 方法进入等待状态,直到另一个线程调用该Event的set()方法将内置标志设置为True时(或超过wait的等待时间),该线程才会恢复运行。

python线程的事件Event用于主线程控制其他线程的执行,事件主要提供了三个方法wait、clear、set。

事件处理的机制:全局定义了一个“Flag”,如果“Flag”值为 False,那么当程序执行 event.wait 方法时就会阻塞,如果“Flag”值为True,那么event.wait 方法时便不再阻塞。默认为 False。
clear:将“Flag”设置为False,阻塞线程。
set:将“Flag”设置为True,唤醒线程,不再阻塞。
isSet():查询此时的“Flag”状态,返回 True 或者 False

代码示例,把代码文件放入 cmd 中运行,能看到更好的运行效果。

import  threading
import itertools
import time, sys


def spin(msg, done):    # 在单独线程中运行
    write, flush = sys.stdout.write, sys.stdout.flush
    for char in itertools.cycle('|/-\\'):    # |/—\|
        status = char + ' ' + msg
        write(status)     # 写入到标准输出中,即可以在cmd里显示。
        flush()              # 刷新缓存,使其马上显示到屏幕上。
        write('\x08'*len(status))    # \x08 退格符和\b等效。
        # if  done.wait(0.1):    # 阻塞,直到done事件被设置为True 或 超时(这里设置为只阻塞0.1秒,但此时的done还是False)。
        done.wait(0.1)      # 阻塞线程,直到done事件被设置为True 或 超时(这里设置为只阻塞0.1秒,但此时的done还是False)。
        if done.isSet():    # isSet()判断是 Event 是否为 True。
            break
    #write(' '*len(status) )  #+ '\x08'*len(status)

def slow_function():   # 假设这是 处理 I/O事件的耗时计算。
    time.sleep(5)    # 释放GIL锁给子线程用。
    return 2222


def supervisor():    # 监督线程
    done = threading.Event()
        # event对象维护一个内部标志(初始为False),通过set()将其设置为True,
        # wait(timeout) 用于堵塞线程直至Flag被set(可选的),clear()用于清除标志(使之为False)。
    spinner = threading.Thread(target=spin, args=('thinking', done))
    print(f'spinner object: {spinner}')
    spinner.start()
    result = slow_function()    # 在主线程运行 I/O 计算。
    done.set()  # 设置done事件为True。
    spinner.join()
    return result
    

def main():
    result = supervisor()
    print(f'Answer: {result}', flush=True)
    
if __name__ == '__main__'
	main()

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值