网络编程
位置:首页>> 网络编程>> Python编程>> Python多线程编程之threading模块详解

Python多线程编程之threading模块详解

作者:汪酱努力提升打工ing  发布时间:2023-12-28 07:52:59 

标签:python,threading,模块,多线程

一、介绍

线程是什么?线程有啥用?线程和进程的区别是什么?

线程是操作系统能够进行运算调度的最小单位。被包含在进程中,是进程中的实际运作单位。一条线程指的是进程中一个单一顺序的控制流,一个进程中可以并发多个线程,每条线程并行执行不同的任务。

二、Python如何创建线程

2.1 方法一:

创建Thread对象

步骤:

1.目标函数

2.实例化Thread对象

3.调用start()方法



import threading

# 目标函数1
def fun1(num):
   for i in range(num):
       print('线程1: 第%d次循环:' % i)

# 目标函数2
def fun2(lst):
   for ele in lst:
       print('线程2: lst列表中元素 %d' % ele)

def main():
   num = 10
   # 实例化Thread对象
   # target参数一定为一个函数,且不带括号
   # args参数为元组类型,参数为一个时一定要加逗号
   t1 = threading.Thread(target=fun1, args=(num,))
   t2 = threading.Thread(target=fun2, args=([1, 2, 3, 4, 5],))

# 调用start方法
   t1.start()
   t2.start()

if __name__ == '__main__':
   main()

2.2 方法二:

创建子类继承threading.Thread类


import threading
import os

class Person(threading.Thread):
   def run(self):
       self.sing(5)
       self.cook()

@staticmethod
   def sing(num):
       for i in range(num):
           print('线程[%d]: The person sing %d song.' % (os.getpid(), i))

@staticmethod
   def cook():
       print('线程[%d]:The person has cooked breakfast.' % os.getpid())

def main():
   p1 = Person()
   p1.start()

p2 = Person()
   p2.start()

if __name__ == '__main__':
   main()

三、线程的用法

3.1 确定当前的线程


import threading
import time
import logging

def fun1():
   print(threading.current_thread().getName(), 'starting')
   time.sleep(0.2)
   print(threading.current_thread().getName(), 'exiting')

def fun2():
   # print(threading.current_thread().getName(), 'starting')
   # time.sleep(0.3)
   # print(threading.current_thread().getName(), 'exiting')
   logging.debug('starting')
   time.sleep(0.3)
   logging.debug('exiting')

logging.basicConfig(
   level=logging.DEBUG,
   format='[%(levelname)s] (%(threadName)-10s) %(message)s'
)

def main():
   t1 = threading.Thread(name='线程1', target=fun1)
   t2 = threading.Thread(name='线程2', target=fun2)
   t1.start()
   t2.start()

if __name__ == '__main__':
   main()

3.2 守护线程

区别

  •  普通线程:主线程等待子线程关闭后关闭

  • 守护线程:管你子线程关没关,主线程到时间就关闭

守护线程如何搞

  • 方法1:构造线程时传入dameon=True

  • 方法2:调用setDaemon()方法并提供参数True


import threading
import time
import logging

def daemon():
   logging.debug('starting')
   # 添加延时,此时主线程已经退出,exiting不会打印
   time.sleep(0.2)
   logging.debug('exiting')

def non_daemon():
   logging.debug('starting')
   logging.debug('exiting')

logging.basicConfig(
   level=logging.DEBUG,
   format='[%(levelname)s] (%(threadName)-10s) %(message)s'
)

def main():
   # t1 = threading.Thread(name='线程1', target=daemon)
   # t1.setDaemon(True)
   t1 = threading.Thread(name='线程1', target=daemon, daemon=True)
   t2 = threading.Thread(name='线程2', target=non_daemon)
   t1.start()
   t2.start()

# 等待守护线程完成工作需要调用join()方法,默认情况join会无限阻塞,可以传入浮点值,表示超时时间
   t1.join(0.2)
   t2.join(0.1)

if __name__ == '__main__':
   main()

3.3控制资源访问

目的:

Python线程中资源共享,如果不对资源加上互斥锁,有可能导致数据不准确。


import threading
import time

g_num = 0

def fun1(num):
   global g_num
   for i in range(num):
       g_num += 1
   print('线程1 g_num = %d' % g_num)

def fun2(num):
   global g_num
   for i in range(num):
       g_num += 1
   print('线程2 g_num = %d' % g_num)

def main():
   t1 = threading.Thread(target=fun1, args=(1000000,))
   t2 = threading.Thread(target=fun1, args=(1000000,))
   t1.start()
   t2.start()

if __name__ == '__main__':
   main()
   time.sleep(1)
   print('主线程 g_num = %d' % g_num)

互斥锁


import threading
import time

g_num = 0
L = threading.Lock()

def fun1(num):
   global g_num
   L.acquire()
   for i in range(num):
       g_num += 1
   L.release()
   print('线程1 g_num = %d' % g_num)

def fun2(num):
   global g_num
   L.acquire()
   for i in range(num):
       g_num += 1
   L.release()
   print('线程2 g_num = %d' % g_num)

def main():
   t1 = threading.Thread(target=fun1, args=(1000000,))
   t2 = threading.Thread(target=fun1, args=(1000000,))
   t1.start()
   t2.start()

if __name__ == '__main__':
   main()
   time.sleep(1)
   print('主线程 g_num = %d' % g_num)

互斥锁引发的另一个问题:死锁

死锁产生的原理:

Python多线程编程之threading模块详解


import threading
import time

g_num = 0
L1 = threading.Lock()
L2 = threading.Lock()

def fun1():
   L1.acquire(timeout=5)
   time.sleep(1)
   L2.acquire()
   print('产生死锁,并不会打印信息')
   L2.release()
   L1.release()

def fun2():
   L2.acquire(timeout=5)
   time.sleep(1)
   L1.acquire()
   print('产生死锁,并不会打印信息')
   L1.release()
   L2.release()

def main():
   t1 = threading.Thread(target=fun1)
   t2 = threading.Thread(target=fun2)
   t1.start()
   t2.start()

if __name__ == '__main__':
   main()
   time.sleep(1)
   print('主线程 g_num = %d' % g_num)

如何避免产生死锁:

锁超时操作


import threading
import time

g_num = 0
L1 = threading.Lock()
L2 = threading.Lock()

def fun1():
   L1.acquire()
   time.sleep(1)
   L2.acquire(timeout=5)
   print('超时异常打印信息1')
   L2.release()
   L1.release()

def fun2():
   L2.acquire()
   time.sleep(1)
   L1.acquire(timeout=5)
   print('超时异常打印信息2')
   L1.release()
   L2.release()

def main():
   t1 = threading.Thread(target=fun1)
   t2 = threading.Thread(target=fun2)
   t1.start()
   t2.start()

if __name__ == '__main__':
   main()
   time.sleep(1)
   print('主线程 g_num = %d' % g_num)

来源:https://blog.csdn.net/qq_31385393/article/details/115873831

0
投稿

猜你喜欢

手机版 网络编程 asp之家 www.aspxhome.com