python多线程

目录

(0)重要名词

(1)如何创建线程

用_thread模块

用threading模块

(2)线程对象的join方法

(3)多线程共享全局变量

(4)线程冲突

(5)用锁解决线程冲突

(6)用with简化锁操作

(7)死锁

(8)用Rlock创建锁避免死锁

(9)线程信号量(限制最大线程数量)

(10)线程屏障(所需最低线程数量)

(11)线程事件

(12)线程同步和线程通信

(13)condition

(14)线程调度

(15)生产者消费者模式

(16)线程池

(17)定时线程

(18)后台线程

(19)TLS(线程独立存储空间)


python多线程通过threading模块实现,支持并发执行任务,但受GIL限制,适合I/O密集型场景。

(0)重要名词

线程冲突  →  锁  → 锁与with → 死锁 → Rlock

线程信号量 vs 锁定线程数量

线程同步与线程通信

Lock、Event 、Condition

线程调度

生产者消费者模式

线程池

定时线程

后台线程

线程独立空间(TLS)

(1)如何创建线程

用_thread模块

# 普通运行的程序都是主线程,按代码顺序运行

# 多线程,就是使用特定模块(此处用的_thread模块)创建了几个次线程(小弟线程),并发运行

# 使用_thread创建的次线程,与后面的主线程同时运行,当主线程结束时,若次线程还未结束,会被强制结束

import win32api  # 引用系统函数
import _thread   # 创建多线程的一种模块

def show(num):
    win32api.MessageBox(0, "你的账户很危险"+str(num), "来自支付宝的问候", num)

# 顺序执行
# for i in range(5):
#     show(i)

# 伪并发
for i in range(5):
    _thread.start_new_thread(show, (i,))  
# 第二参数为元组,用于传递show函数的参数,单个参数时仍以元组形式,比如(3,)

show(5)      
while True:  # 让主线程不死   # 主线程不死,子线程就不会死
    pass

用threading模块

①直接使用线程类接口函数创建多线程

import threading
import win32api

def show(i):
    win32api.MessageBox(0, "你的账户很危险" + str(i), "来自支付宝的问候", 6)
    print(threading.current_thread().name)   # threading.current_thread().name返回当前线程的名称

threading.Thread(target = show, args = (2,)).start()  # 匿名线程对象
for i in range(5):
    threading.Thread(target = show, args = (i,)).start()

②主线程会等待子线程全部运行完才会结束

以下示例代码会演示,主线程并不会结束,而是要等子线程结束后才会结束

import time
import threading

def write_data(num):
    time.sleep(10)
    file = open('./data.txt', 'a')
    file.write(f"{num}\n")
    file.close()

th = threading.Thread(target=write_data, args=(100,))
th.start()

③继承线程类重写run函数来创建多线程

import threading
import win32api 

class Mythread(threading.Thread): #继承threading.Thread实现多线程
    def __init__(self, num):
        threading.Thread.__init__(self) # 父类初始化
        self.num = num

    def run(self): # run重写
        win32api.MessageBox(0, "你的账户很危险" + str(self.num), "来自支付宝的问候", 6)

for i in range(5):
    t = Mythread(i) # 初始化
    t.start()       # 开启

④获取线程名字

线程对象self.name或者self.getName(),返回当前线程名字

import threading
import win32api


class Mythread(threading.Thread): #继承threading.Thread实现多线程
    def __init__(self, num):
        threading.Thread.__init__(self)  # 父类初始化
        self.num = num

    def run(self):  # run重写
        win32api.MessageBox(0, "你的账户很危险" + str(self.num), "来自支付宝的问候"+self.name, 6)


for i in range(5):
    t = Mythread(i)  # 初始化
    t.start()        # 开启

(2)线程对象的join方法

  • 若某子线程使用了join方法,则主线程要等到该线程执行完毕后才能继续执行
  • 若子线程未使用join方法,即不阻塞,则主线程和子线程同时运行,同时若主线程先运行还会等待子线程运行完,整个程序才会结束

①顺序执行

import threading
import win32api

class Mythread(threading.Thread):
    def run(self):
        win32api.MessageBox(0, "你的账户很危险", "来自支付宝的问候", 6)

for i in range(5):
    t = Mythread() # 创建线程对象
    t.start() # 线程开启
    t.join()  # 主线程等待线程t执行完成

print("当无join时,这句话在对话框结束前执行;当有join时,这句话在对话框结束后执行")

②并发执行

import threading
import win32api  

class Mythread(threading.Thread):
    def run(self):
        win32api.MessageBox(0, "你的账户很危险", "来自支付宝的问候", 6)

# 同时运行5个次线程
threads = [] # 集合list
for i in range(5):
    t = Mythread() # 初始化
    t.start()
    threads.append(t) #加入线程集合

# 使用线程类的join函数,实现“执行完所有同时运行的次线程后再运行主线程”
for mythd in threads:  # mythd是一个线程
    mythd.join()       # 主线程等待线程t执行完成,不需要阻塞
print("game over")

# 这里有join,子线程全部运行完毕后,才会打印game over
# 如果没有join,则会立即打印game over,同时子线程也继续运行,
#                                  子线程运行完,整个程序才会结束

(3)多线程共享全局变量

对于全局变量x,每个线程访问和修改x时,操作的是同一个x

import threading

data = 10

def func(num):
    global data
    data += num

for i in range(5):
    t = threading.Thread(target=func, args=(i,))
    t.start()
    t.join()
    print(data)

(4)线程冲突

# 什么是线程冲突

# 线程冲突,多个线程同时访问同一个资源使得结果在逻辑上出现错误,尽管没有弹出异常

import threading
import time

num = 0
def add():
    global num
    for i in range(1000000):
        num += 1
    print(num)


# 目标:执行五次add函数使得num的值变为5000000
for i in range(5):
    add()

# 如果使用多线程会产生错误
num = 0
print("-"*20)
for i in range(5):
    t = threading.Thread(target = add, args = ())
    t.start()

(5)用锁解决线程冲突

# mutex = threading.Lock() # 创建锁,默认是开锁状态

# mutex.acquire(True)  # 对锁进行锁住尝试,若锁住成功继续干活,并且返回True,没有锁住成功就一直请求

# 当锁是锁住状态时,锁住尝试将会不成功

# mutex.acquire的参数中的blocking布尔变量,是否锁住;timeout浮点数,锁住几秒,默认一直锁住

# mutex.release() # 释放锁   # 当释放锁后,其他人就可以锁住了

# 锁住的好处,比如写1000个线程爬虫,爬虫可以同步,但在往文件里写东西时不能同时写,必须要锁住(向同一个文件写东西时经常用锁)

import threading
import time

num = 0
mutex = threading.Lock()
class Mythread(threading.Thread):
    def run(self):
        global num
        if mutex.acquire(True):
            for i in range(1000000):
                num += 1
            mutex.release()
        print(num)

print("game start")
threads = []
for i in range(5):
    t = Mythread()
    t.start()
    threads.append(t)

for i in range(len(threads)):
    threads[i].join()
print("game over")

(6)用with简化锁操作

# with就是锁“请求”和“释放”的结合,更简洁

import threading

mutex = threading.Lock()
num = 0  # 全局变量多个线程可以读写,传递数据

class addthread(threading.Thread):
    # def __init__(self):
    #     threading.Thread.__init__(self)

    def run(self):
        global num
        with mutex:
           for i in range(1000000):
               num += 1
        print(num)

threads = []
for i in range(5):
    t = addthread()
    t.start()
    threads.append(t)

for t in threads:
    t.join()

print("game over")

(7)死锁

# 死锁就是锁本身处于锁定状态,但是却请求锁定操作,无法成功的情况,因为根据锁的锁定原理,只有锁处于释放状态时,才能锁定成功

# 例子,男生要等到女方道歉才肯道歉,女生要等到男生道歉才肯道歉,于是产生死锁

# 解决办法,比如女方先道歉,则在锁住后执行相关命令后,就释放锁,而不是要等到男方释放锁以后才释放锁

import threading
import time

boymutex = threading.Lock()   # 创建男孩的锁
girlmutex = threading.Lock()  # 创建女孩的锁

# 男孩线程类
class boythread(threading.Thread):
    def run(self):
        if boymutex.acquire(1):
            print(self.name + "男方尝试道歉,但想等女方道歉后再道歉")
            time.sleep(3)

            if girlmutex.acquire(1):
                print(self.name + "girl say I am sorry")
                girlmutex.release()
            boymutex.release()

# 女孩线程类
class girlthread(threading.Thread):
    def run(self):
        if girlmutex.acquire(1):
            print(self.name + "女方尝试道歉,但想等男方道歉后再道歉")  # self.name线程名称
            time.sleep(3)

            #girlmutex.release()    # 让女孩先道歉,可以解决死锁问题
            if boymutex.acquire(1):
                print(self.name+"boy say I am sorry")
                boymutex.release()
            girlmutex.release()

#开启两个线程
bt = boythread()
bt.start()
gt = girlthread()
gt.start()

(8)用Rlock创建锁避免死锁

# 使用Rlock创建的锁可以避免单线程死锁

# 单线程死锁:对一个线程加锁再加锁

import threading

num = 0
mutex = threading.RLock()

class Mythread(threading.Thread):
    def run(self):
        global num
        if mutex.acquire(1):
            print(self.name, num)

            if mutex.acquire(1):
                num += 1000
                mutex.release()

            mutex.release()

for i in range(5):  # 开启五个线程
    t = Mythread()
    t.start()

(9)线程信号量(限制最大线程数量)

①线程信号量的本质和原理

多线程同时运行,能提高程序的运行效率,但是并非线程越多越好,而semaphore信号量可以通过内置计数器来控制同时运行线程的数量,启动线程(消耗信号量)内置计数器会自动减一,线程结束(释放信号量)内置计数器会自动加一;内置计数器为零,启动线程会阻塞,直到有本线程结束或者其他线程结束为止;

信号量本质上还是锁,只不过这个锁有很多把钥匙,同一时间只允许有限个线程进程操作(实现有限个数据的并发),但是线程数开了很多个的(信号量的参数不是开的线程数。只是代表同一时间允许并发的线程数)

②semaphore信号量相关函数介绍

  • acquire() — 消耗信号量,内置计数器减一;
  • release() — 释放信号量,内置计数器加一;

在semaphore信号量有一个内置计数器,控制线程的数量,acquire()会消耗信号量,计数器会自动减一;release()会释放信号量,计数器会自动加一;当计数器为零时,acquire()调用被阻塞,直到release()释放信号量为止。

示例1

# 以爬虫为例,不可能每个网页都创建一个线程,因为内存有限,所以要限定线程数量

import threading
import time

sem2 = threading.Semaphore(2) # 限制线程的最大数量为2个
sem4 = threading.Semaphore(4) # 限制线程的最大数量为4个

def gothread():
    with sem4: # 锁定数量
        for i in range(10):
            print(threading.current_thread().name, i)   
            # threading.current_thread().name返回当前线程的名称
            time.sleep(1)

for i in range(5):
    threading.Thread(target = gothread).start()

示例2

创建多个线程,限制同一时间最多运行5个线程,示例代码如下:

# 导入线程模块
import threading

# 导入时间模块
import time

# 添加一个计数器,最大并发线程数量5(最多同时运行5个线程)
semaphore = threading.Semaphore(5)

def foo():
    semaphore.acquire()  # 计数器获得锁
    time.sleep(2)  # 程序休眠2秒
    print("当前时间:", time.ctime())  # 打印当前系统时间
    semaphore.release()  # 计数器释放锁

if __name__ == "__main__":
    thread_list = list()
    for i in range(20):
        t = threading.Thread(target=foo, args=())  # 创建线程
        thread_list.append(t)
        t.start()  # 启动线程
    for t in thread_list:
        t.join()
    print("程序结束!")

输出结果:

当前时间: Wed Oct 23 22:21:59 2019
当前时间: Wed Oct 23 22:21:59 2019
当前时间: Wed Oct 23 22:21:59 2019
当前时间: Wed Oct 23 22:21:59 2019
当前时间: Wed Oct 23 22:21:59 2019
当前时间: Wed Oct 23 22:22:01 2019
当前时间: Wed Oct 23 22:22:01 2019
当前时间: Wed Oct 23 22:22:01 2019
当前时间: Wed Oct 23 22:22:01 2019
当前时间: Wed Oct 23 22:22:01 2019
当前时间: Wed Oct 23 22:22:03 2019
当前时间: Wed Oct 23 22:22:03 2019
当前时间: Wed Oct 23 22:22:03 2019
当前时间: Wed Oct 23 22:22:03 2019
当前时间: Wed Oct 23 22:22:03 2019
当前时间: Wed Oct 23 22:22:05 2019
当前时间: Wed Oct 23 22:22:05 2019
当前时间: Wed Oct 23 22:22:05 2019
当前时间: Wed Oct 23 22:22:05 2019
当前时间: Wed Oct 23 22:22:05 2019
程序结束!

根据打印的日志可以看出,同一时间只有5个线程运行,间隔两秒之后,再次启动5个线程,直到20个线程全部运行结束为止;如果没有设置信号量Semapaore,创建线程直接start(),输出的时间全部都是一样的,这个问题比较简单,可以自己去实验一下!

(10)线程屏障(所需最低线程数量)

达到指定线程数量才能执行某项任务

# 比如开启1000个线程,但必须每次要有3个线程的数据时才能写到一个文件,缺一不可

# 比如由1000个线程,先找3个线程运行,再找3个运行,当最后不够3个时,就等着

# 只有线程数量为3的整数倍时程序能正常运行完

import time
import threading

bar = threading.Barrier(3)  # 必须凑到3个才能一起执行

# help(bar)  → abort(self)  reset(self)  wait(self, timeout=None)
# help(bar)

def sever():
    print(threading.current_thread().name, "start")   
    # 打印当前线程的名称
    time.sleep(5)                                     
    # 5秒的缓冲时间足以打印完所有线程的名称

    bar.wait()   # 锁定线程匹配数量,只有达到指定线程数量时才可继续运行
    print(threading.current_thread().name, "end")

for i in range(8):
    threading.Thread(target = sever).start()

(11)线程事件

# 用threading.Event()来创建一个事件标志,默认值为False,可以让子线程来调用该标志具有的方法来是否阻塞该子线程

Event和Lock的区别:锁在同一时间只放一个线程通行,而事件的作用要么阻塞全部线程全部放全部线程通行。

# event对象主要有四种方法可以调用:

event.wait(timeout=None)

调用该方法的线程会被阻塞,如果设置了timeout参数,超时后,线程会停止阻塞继续执行

event.set()

将event的标志设置为True,调用wait方法的所有线程将被唤醒

event.clear()

将event的标志设置为False,调用wait方法的所有线程将被阻塞(不用此方法会使得wait方法只生效一次,因此在循环中使用wait方法时,记得还要加上此方法)

event.isSet()

判断event的标志是否为True

例子

import threading
import time

e = threading.Event() # 标志

def go(event):
    event.wait()     # 阻塞线程,后续代码不再执行,等待命令set后再执行
    print("go")

threading.Thread(target = go, args = (e,)).start() # 开启一个线程
time.sleep(5)
e.set()             # 唤醒线程

(12)线程同步和线程通信

# 线程冲突 → 解决办法:锁

# 使用锁后,这种一个线程“等待”另一个线程的过程,就是线程同步

# 线程同步和线程通信的区别:线程同步谁先抢到就是谁的,线程通信是指定下一个线程

# 通常使用lock产生线程同步,用event产生线程通信

例子:线程通信

# 线程通信例子
import threading
import time

class mythread(threading.Thread):
    def __init__(self):
        threading.Thread.__init__(self)
        self.event = threading.Event()

    def run(self):
        global mystr
        print(self.name + "start")
        self.event.wait()        # 也可以将此阻塞语句放在主线程里面设计
        mystr += "  " + self.name
        print(self.name, mystr)

# 用mystr变量来记录子线程的唤醒顺序
mystr = "order:"   

threads = []
for i in range(5):
    t = mythread()
    threads.append(t)
    t.start()

# 按指定顺序唤醒子线程
orderList = [2, 1, 4, 0, 3]   
for i in orderList:
    time.sleep(3)
    threads[i].event.set()

for thd in threads:
    thd.join()

print(mystr)

(13)condition

# condition:像lock和event的结合体,兼顾线程同步和线程通信

①像锁一样

import threading
import time

cond = threading.Condition() # 线程条件变量

def go1():
    with cond:  #锁定cond期间,线程2的go2不能使用cond,这里就像锁一样
        for i in range(10):
            time.sleep(1)
            print(threading.current_thread().name, i)

def go2():
    with cond:
        for i in range(10):
            time.sleep(1)
            print(threading.current_thread().name, i)

threading.Thread(target = go1).start()
threading.Thread(target = go2).start()

②结合lock和event

import threading
import time

cond = threading.Condition() # 线程条件变量

def go1():
    with cond:  #锁定cond期间,线程2的go2不能使用cond,这里就像锁一样
        for i in range(10):
            time.sleep(1)
            print(threading.current_thread().name, i)
            if i == 5:
               cond.wait() #像event的wait方法一样阻塞调用该方法的线程  
               #使用wait方法后线程2的go2可以使用cond

def go2():
    with cond:
        for i in range(10):
            time.sleep(1)
            print(threading.current_thread().name, i)
        cond.notify()  #像event的set方法一样唤醒因wait方法被阻塞的线程

threading.Thread(target = go1).start()
threading.Thread(target = go2).start()

(14)线程调度

①两个线程交替执行

# 面试经常问到的问题:两个线程如何交替执行

# 例子:

# 线程1:打印0 2 4 6 8

# 线程2:打印1 3 5 7 9

# 实现:线程1打印0 → 线程2打印1 → 线程1打印2 → 线程2打印3 ..... 线程1打印8 → 线程2打印9

# A线程阻塞在wait方法时,只有B线程执行了notify方法A线程才能继续执行

# 反过来,B线程同理

import threading
import time

def go1():
    with cond:
        for i in range(0, 10, 2):
            time.sleep(1)
            print(threading.current_thread().name, i)

            cond.wait()    # 当前线程被阻塞,要等到被释放后才能继续运行  
            # 使用wait方法后其他线程可以使用cond
            cond.notify()

def go2():
    with cond:
        for i in range(1, 10, 2):
            time.sleep(1)
            print(threading.current_thread().name, i)

            cond.notify()  # 释放被wait方法阻塞的线程(释放了go1所在的线程)
            cond.wait()    # 当前线程被阻塞

cond = threading.Condition() # 线程条件变量
threading.Thread(target = go1).start()
threading.Thread(target = go2).start()

②多个线程交替执行

思路就是给每个线程都配一把锁,执行某个线程前就开启该线程的锁,同时其它线程的锁全部锁住。

import threading
import time

class printThread(threading.Thread):
    def __init__(self, index):
        super().__init__()
        self.index = index

    def run(self):
        while True:
            while threads_locks[self.index][1].acquire():
                time.sleep(0.5)
                # 工作内容
                print(threading.current_thread().name, self.index+1) 
 
                if self.index < 4:
                    threads_locks[self.index+1][1].release()
                elif self.index == 4:
                    threads_locks[0][1].release()

# 创建线程和锁
threads_locks = []
for i in range(5):
    t = printThread(i)
    lock = threading.Lock()
    threads_locks.append((t, lock))

# 初始只开启第一个锁,其余锁全部锁住
for i in range(1, len(threads_locks)):
    threads_locks[i][1].acquire()

for t_lock in threads_locks:
    t_lock[0].start()

for t_lock in threads_locks:
    t_lock[0].join()

print("game over")

(15)生产者消费者模式

# 队列一端的压入为生产,另一端取出为消费

# 队列的创建方法queue,压入方法put,取出方法get

生产者消费者模式的意思就是,创建两种线程类,一种线程类负责生产,并将生产的结果压入队列,而另一种线程负责从队列中取出结果,执行对应的操作。

import threading
import queue
import time
import random

#生产
class  creatorThread(threading.Thread):
    def  __init__(self, index, myqueue):
        threading.Thread.__init__(self)
        self.index = index   #生产者代号
        self.myqueue = myqueue  #队列

    def run(self):
        while True:
            time.sleep(3) #三秒生产一个
            num = random.randint(1, 1000000) #随机数
            self.myqueue.put("生产者" + str(self.index) + "硅胶娃娃" + str(num))
            print("生产者" + str(self.index) + "硅胶娃娃" + str(num))
        #self.myqueue.task_done() #完成任务

#消费
class  buyerThread(threading.Thread):
    def  __init__(self,index, myqueue):
        threading.Thread.__init__(self)
        self.index = index   #消费者代号
        self.myqueue = myqueue  #队列

    def run(self):
        while True:
            time.sleep(1)
            item = self.myqueue.get() #抓取数据
            if item is None:
                break
            print("客户", self.index, "买到物品", item)
        #self.myqueue.task_done()  # 完成任务


myqueue = queue.Queue(10) #0代表无限,10最大容量
for i in range(3):  #3个生产者线程
    creatorThread(i, myqueue).start()
for i in range(8):   #8个消费者线程
    buyerThread(i, myqueue).start()

(16)线程池

①线程池的作用

Python 常常使用线程池来加速代码的执行。线程池是一种预先创建的线程集合,可以用来运行大量的并发任务。Python 中的 GIL(全局解释器锁)使得 Python 解释器在任何时候只能执行一个线程,这在一些情况下会令 Python 代码性能下降。但是,使用线程池可以缓解这种问题,尤其在 CPU 密集任务下会更为明显。

线程池可以提供以下几个好处:

  1. 减少线程创建的开销:线程创建和销毁期间的操作需要占用额外的系统资源,使用线程池可以避免这种操作。
  2. 管理线程执行:线程池可以更好地控制线程的数量和优先级,更好地管理运行的线程。
  3. 将并发性和并行性分离:线程池利用并行性和并发性,而将它们分离会使代码更容易理解和维护。
  4. 避免竞争条件:线程池可以显式地控制共享资源,从而避免竞争条件。

总的来说,线程池的使用可以提高 Python 代码的性能,并帮助 Python 解释器更好地管理运行的线程。

②安装和基本使用

安装
pip install threadpool

基本使用
import threadpool
pool = threadpool.ThreadPool(poolsize)
定义了一个线程池,表示最多可以创建poolsize这么多线程

requests = threadpool.makeRequests(some_callable, list_of_args, callback)
创建了要开启多线程的函数,以及函数相关参数和回调函数,其中回调函数可以不写,default是无

[pool.putRequest(req) for req in requests]
将所有要运行多线程的请求扔进线程池

pool.wait()
等待所有的线程完成工作后退出,类似join的作用

例子1:函数只有一个参数时

import time
import threadpool

def show(str):
    time.sleep(1)
    print("hello", str)

namelist = ["高清华", "hefengcheng", "sunyu", "gogogo"]
start_time = time.time() #开始时间
pool = threadpool.ThreadPool(10) #最大容量10个
requests=threadpool.makeRequests(show, namelist) #任务函数,参数列表
for req in requests:
    pool.putRequest(req) #将每一个请求压入线程池开始执行
pool.wait()
end_time = time.time() #结束时间
print(end_time - start_time)

例子2:函数有多个参数时

import time
import threadpool

def hello(x, y, z):
    time.sleep(2)
    print("x = {}, y = {}, z = {}".format(x, y, z))

if __name__ == '__main__':
    #方法1:将参数解析为列表
    list_vars_1 = ['1', '2', '3']
    list_vars_2 = ['4', '5', '6']
    fun_var = [(list_vars_1, None), (list_vars_2, None)]

    # 方法2:将参数解析为字典
    # dict_vars_1 = {"x": "1", "y": "2", "z": "3"}
    # dict_vars_2 = {"x": "4", "y": "5", "z": "6"}
    # fun_var = [(dict_vars_1, None), (dict_vars_2, None)]

    start = time.time()

    pool = threadpool.ThreadPool(3)
    requests = threadpool.makeRequests(hello, fun_var)
    [pool.putRequest(req) for req in requests]
    pool.wait()

    end = time.time()
    print(end - start)

例子3:先转换参数形式,再带入线程池

def getuserdic():
    username_list = ['xiaozi', 'administrator']
    password_list = ['root', '', 'abc123!', '123456', 'password', 'root']
    userlist = []

    for username in username_list:
        user = username.rstrip()
        for password in password_list:
            pwd = password.rstrip()
            userdic = {}
            userdic['user'] = user
            userdic['pwd'] = pwd
            tmp = (None, userdic)
            userlist.append(tmp)
    return userlist

(17)定时线程

# 定时线程,就是指定某个时间长度后才执行的线程

import threading
import time
import os

def go():
    os.system("notepad")

timethread = threading.Timer(5, go)  # 5秒后线程执行
timethread.start()

(18)后台线程

# threading.Tread创建的子线程,主线程必须等待所有子线程结束后才会结束,这种子线程被称为前台线程

# 后台线程则是指该子线程无论是否结束,主线程运行完毕后就会结束

import threading
import win32api

class messageThread(threading.Thread):
    # def __init__(self):
    #     threading.Thread.__init__(self)

    def run(self):
        win32api.MessageBox(0, "来自支付宝的问候", "你的账户很危险", 6)

for i in range(5):
    t = messageThread()
    t.setDaemon(True)  # 设置为后台线程,主线程不等后台线程
    t.start()

print("game over")

(19)TLS(线程独立存储空间)

# TLS: Thread Local Space,线程之间相互独立的存储空间

import threading
import time

data = threading.local()  #线程之间相互独立的存储空间

f1 = lambda x : x+1
f2 = lambda x : x+"1"

def printdata(func, x):
    data.x = x   #data是一个类,动态绑定,线程独立  #data.x每个线程中是独立
    print(id(data.x))  # 不同的地址
    for i in range(5):
        data.x = func(data.x)
        # time.sleep(1)
        print(threading.current_thread().name, data.x)

threading.Thread(target=printdata, args=(f1, 1)).start()
threading.Thread(target=printdata, args=(f2, "1")).start()


end

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值