Python 多线程的使用

本文详细介绍了Python中多线程的实现方式,包括使用Threading模块创建线程的方法,以及如何通过继承Thread类来定制线程行为。探讨了线程与进程的区别,全局变量在多线程中的共享特性,以及如何利用线程锁解决多线程环境下数据安全问题。同时,讨论了线程锁的优缺点和如何避免死锁,提供了具体的代码示例。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

        多线程,python中创建多线程模块是Threading模块,Python中Thread是比较底层的模块,Threading是对Thread模块的封装。

         首先使用threading模块来创建线程;


  
  1. import threading
  2. def say():
  3. print( "-------子线程-------")
  4. if __name__ == "__main__":
  5. for i in range( 5):
  6. thread = threading.Thread(target=say) # 创建多线程实例,Thread中target是需要子线程执行的函数
  7. thread.start() # 启动多线程

      第二种方式,通过集成的方式来创建线程;


  
  1. import threading
  2. import time
  3. class test_thread(threading.Thread): # 通过继承threading.Thread重写run方法的形式,创建子线程
  4. def run(self):
  5. for i in range( 3):
  6. time.sleep( 1)
  7. msg = "I'm " + self.name
  8. print(msg)
  9. def test():
  10. for i in range( 5):
  11. t = test_thread() # 实例化线程对象
  12. t.start()
  13. if __name__ == "__main__":
  14. test()

运行结果:


  
  1. I 'm Thread-5
  2. I'm Thread -3
  3. I 'm Thread-4
  4. I'm Thread -2
  5. I 'm Thread-1

共享全局变量

    线程还有一个特点是,全局变量是共享的,不同于进程全局变量之间是不同享,想要使用被改变的变量,需要采取一定的措施(进程之间的通信),才能共享数据。

线程与进程的区别:

定义的不同

1.进程是系统进⾏资源分配和调度的⼀个独⽴单位.

2.线程是进程的⼀个实体,是CPU调度和分派的基本单位,它是⽐进程更⼩的 能独⽴运⾏的基本单位.线程⾃⼰基本上不拥有系统资源,只拥有⼀点在运 ⾏中必不可少的资源(如程序计数器,⼀组寄存器和栈),但是它可与同属⼀个进程的其他的线程共享进程所拥有的全部资源.

区别

⼀个程序⾄少有⼀个进程,⼀个进程⾄少有⼀个线程.

线程的划分尺度⼩于进程(资源⽐进程少),使得多线程程序的并发性⾼。

进程在执⾏过程中拥有独⽴的内存单元,⽽多个线程共享内存,从⽽极 ⼤地提⾼了程序的运⾏效率

线线程不能够独⽴执⾏,必须依存在进程中

优缺点

线程和进程在使⽤上各有优缺点:线程执⾏开销⼩,但不利于资源的管理和 保护;⽽进程正相反。

线程锁Lock

缺点 就是,线程是对全局变量随意遂改可能造成多线程之间对全局变量 的混乱 (即线程⾮安全)

怎么样解决线程非安全这个问题呢?这里就引出了线程锁的概念

    当多个线程⼏乎同时修改某⼀个共享数据的时候,需要进⾏同步控制
线程同步能够保证多个线程安全访问竞争资源,最简单的同步机制是引⼊互 斥锁。
互斥锁为资源引⼊⼀个状态:锁定/⾮锁定。

某个线程要更改共享数据时,先将其锁定,此时资源的状态为“锁定”,其他 线程不能更改;直到该线程释放资源,将资源的状态变成“⾮锁定”,其他的 线程才能再次锁定该资源。互斥锁保证了每次只有⼀个线程进⾏写⼊操作, 从⽽保证了多线程情况下数据的正确性。

threading模块中定义了Lock类,可以⽅便的处理锁定;


  
  1. from threading import Thread, Lock
  2. import time
  3. g_num = 0
  4. def test1():
  5. global g_num
  6. # 这个线程和test2线程都在抢着 对这个锁 进行上锁,如果有1方成功的上锁,那么导致另外
  7. # 一方会堵塞(一直等待)到这个锁被解开为止
  8. mutex.acquire()
  9. for i in range( 1000000):
  10. g_num += 1
  11. mutex.release() # 用来对mutex指向的这个锁 进行解锁,,,只要开了锁,那么接下来会让所有因为
  12. # 这个锁 被上了锁 而堵塞的线程 进行抢着上锁
  13. print( "---test1---g_num=%d" % g_num)
  14. def test2():
  15. global g_num
  16. mutex.acquire()
  17. for i in range( 1000000):
  18. g_num += 1
  19. mutex.release()
  20. print( "---test2---g_num=%d" % g_num)
  21. # 创建一把互斥锁,这个锁默认是没有上锁的
  22. mutex = Lock()
  23. p1 = Thread(target=test1)
  24. p1.start()
  25. p2 = Thread(target=test2)
  26. p2.start()
  27. print( "---g_num=%d---" % g_num)

运行结果:


  
  1. C:\Users\Admin\PycharmProjects\BlockChain\venv\Scripts\python.exe C:/Users/Admin/PycharmProjects/BlockChain/Thread/threadingtest.py
  2. ---g_num= 172105---
  3. ---test1---g_num= 1000000
  4. ---test2---g_num= 2000000

  总结:

  锁的好处:
     确保了某段关键代码只能由⼀个线程从头到尾完整地执⾏,这里就不会出现如果一个线程更改完数据后,没有生效时,而第二个线程再次更改数据,避免了多线程共享数据的出错。
锁的坏处:

      阻⽌了多线程并发执⾏,包含锁的某段代码实际上只能以单线程模式执 ⾏,效率就⼤⼤地下降了 由于可以存在多个锁,不同的线程持有不同的锁,并试图获取对⽅持有 的锁时,可能会造成死锁

使用线程互斥锁是解决了多线程共享数据可能出差的问题,但是还有可能会造成死锁。

死锁

     在线程间共享多个资源的时候,如果两个线程分别占有⼀部分资源并且同时 等待对⽅的资源,就会造成死锁。尽管死锁很少发⽣,但⼀旦发⽣就会造成应⽤的停⽌响应。下⾯看⼀个死锁 的例⼦。


  
  1. import threading
  2. import time
  3. class MyThread1(threading.Thread):
  4. def run(self):
  5. if mutexA.acquire():
  6. print(self.name + '----do1---up----')
  7. time.sleep( 1)
  8. if mutexB.acquire(): # mutexB在等待MyTherad2中 mutexBjeisu
  9. print(self.name + '----do1---down----')
  10. mutexB.release() #释放 mutexB锁
  11. mutexA.release() #释放 mutexA
  12. class MyThread2(threading.Thread):
  13. def run(self):
  14. if mutexB.acquire():
  15. print(self.name + '----do2---up----')
  16. time.sleep( 1)
  17. if mutexA.acquire(): # mutexA在等待MyTherad1中 mutexA解锁
  18. print(self.name + '----do2---down----')
  19. mutexA.release() # 释放mutexA锁
  20. mutexB.release() # 释放mutexB锁
  21. mutexA = threading.Lock()
  22. mutexB = threading.Lock()
  23. if __name__ == '__main__':
  24. t1 = MyThread1()
  25. t2 = MyThread2()
  26. t1.start()
  27. t2.start()

运行结果:


  
  1. C:\Users\Admin\PycharmProjects\BlockChain\venv\Scripts\python.exe C:/Users/Admin/PycharmProjects/BlockChain/Thread/threadingtest.py
  2. Thread -1----do1---up----
  3. Thread -2----do2---up----

通过上面的例子可以看出,双反都在等待对方锁的释放,执行结果就没有再往下执行,这就造成了死锁。程序会一直在处于等待的状态。

怎么样来避免这种死锁的发生?这里引入同步的概念

同步

同步就是让多个进程有序的执行,从而避免死锁的发生。例


  
  1. from threading import Thread,Lock
  2. from time import sleep
  3. class Task1(Thread):
  4. def run(self):
  5. while True:
  6. if lock1.acquire():
  7. print( "------Task 1 -----")
  8. sleep( 0.5)
  9. lock2.release()
  10. class Task2(Thread):
  11. def run(self):
  12. while True:
  13. if lock2.acquire():
  14. print( "------Task 2 -----")
  15. sleep( 0.5)
  16. lock3.release()
  17. class Task3(Thread):
  18. def run(self):
  19. while True:
  20. if lock3.acquire():
  21. print( "------Task 3 -----")
  22. sleep( 0.5)
  23. lock1.release()
  24. #使用Lock创建出的锁默认没有“锁上”
  25. lock1 = Lock()
  26. #创建另外一把锁,并且“锁上”
  27. lock2 = Lock()
  28. lock2.acquire()
  29. #创建另外一把锁,并且“锁上”
  30. lock3 = Lock()
  31. lock3.acquire()
  32. t1 = Task1()
  33. t2 = Task2()
  34. t3 = Task3()
  35. t1.start()
  36. t2.start()
  37. t3.start()

在创建锁时,将其他两把锁上锁,只让一个线程来执行,执行完成后接着把下一把锁解开,让三个线程有序的执行。


作者:fw19940314
来源:优快云
原文:https://blog.youkuaiyun.com/fw19940314/article/details/79737214
版权声明:本文为博主原创文章,转载请附上博文链接!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值