python线程

文章介绍了Python的threading模块,强调线程对象与实际线程的区别,通过示例展示了单线程和多线程的执行时间差异,并讨论了线程的并发执行。同时,提到了主线程等待子线程完成的情况,以及如何通过面向对象方式创建线程。文章还指出线程执行顺序的无序性,并通过例子说明了多线程共享全局变量时可能出现的问题和解决方案,如使用互斥锁来避免资源竞争。
摘要由CSDN通过智能技术生成

参考:

https://www.cnblogs.com/aylin/p/5601969.html

https://www.cnblogs.com/huxi/archive/2010/06/26/1765808.html

https://docs.python.org/zh-cn/3.8/library/threading.html


"""
threading模块的使用:
# # cpython解释器运行程序文件的时候会产生一个主线程
# # 主线程执行到threading.Thread()的时候不会产生线程
# # 线程对象不是线程,只是一个实例对象而已
# # 执行到t1.start()的时候才会创建一个子线程,并且绑定任务和运行绑定的任务
# # 三个线程在一个时间段内快速轮流执行——>并发
# # 一般直到子线程任务执行完毕之后主线程才会释放
"""



"""
单线程代码示例:注意耗时
"""
# import time
# 
# def print_hello():
#     print('hello')
#     time.sleep(3)
# 
# if __name__ == '__main__':
#     start_time = time.time()
#     print_hello()
#     end_time = time.time()
#     print(f'总用时{end_time-start_time}')



"""
多线程代码示例:注意耗时,与上方几乎一致
"""
# import time
# import threading
# 
# def print_hello():
#     print('你好')
#     time.sleep(2)
# 
# def print_bad():
#     print('不好')
#     time.sleep(3)
# 
# if __name__ == '__main__':
#     start_time = time.time()
#     t1 = threading.Thread(target=print_hello)
#     t2 = threading.Thread(target=print_bad)
#     t1.start()
#     t2.start()
#     t2.join()
#     end_time = time.time()
#     print(f'总用时{end_time-start_time}')



"""
主线程任务执行完以后,会等待所有子线程任务执行完才会退出
"""
# import threading
# from time import sleep, ctime
#
# def sing():
#     sleep(2)
#     print('正在唱歌')
#
# def dance():
#     sleep(3)
#     print('正在跳舞')
#
# if __name__ == '__main__':  # 主线程
#     print(f'程序开始:{ctime()}')
#     t1 = threading.Thread(target=sing)  # 创建子线程对象,target参数用于绑定线程任务(可调用对象);deamon参数用于设置守护线程;args\kwargs参数用于给线程任务传参
#     t2 = threading.Thread(target=dance)  # 执行到这里还是只有一个主线程
#     print(threading.enumerate(), '@'*20)  # enumerate()以列表形式返回当前所有存活的 Thread 对象
#     t1.start()  # 调用线程对象的start()方法间接开启线程,该方法会安排对象的run()方法在一个独立的控制线程中被调用以运行线程任务。此处主线程不会等待子线程执行完毕,而是继续往下执行
#     print(threading.enumerate())
#     t2.start()
#     # sleep(5)  # 睡眠与否,打印结果有较大区别
#     # print(threading.enumerate())
#     print(f'程序结束:{ctime()}')
#     while True:
#         length = len(threading.enumerate())  # len(threading.enumerate())等同于threading.activeCount()返回正在运行的线程数量
#         print(f'当前线程个数{length}')
#         if length <= 1:
#             break
#         sleep(0.5)



"""
使用面向对象的方式创建线程
"""
# import time
# import threading
#
# class Mythread(threading.Thread):
#     # def __init__(self):
#     #     super(Mythread, self).__init__()  # 如果这里要实现__init__方法,需要重载
#    
#     def run(self) -> None:  # run方法专门用来运行绑定的任务(当执行t.start()时,就会自动调用run方法),此处相当于重写父类的run方法
#         for i in range(3):
#             time.sleep(1)
#             print('help')
#
# t = Mythread()
# t.start()



"""
线程的执行顺序是无序的,线程之间是随机切换的,这是由操作系统决定的,不是由解释器决定的
"""
import time
# import threading
# 
# class Mythread(threading.Thread):
#     def run(self):
#         for i in range(3):
#             time.sleep(1)
#             msg = '我是' + self.name + ' @ ' + str(i)  # 此处的name属性表示当前线程名称,该属性继承自父类,可以改动
#             print(msg)
# 
# def test():
#     for i in range(2):
#         t = Mythread()  # 线程名称改动Mythread(name='线程1')
#         t.start()
#         print(t.name, '@@@@@@@@@')
# 
# if __name__ == '__main__':
#     test()



# ----------------------------多线程共享全局变量-----------------------------
"""
数字类型共享:需要声明global
"""
# import threading
# g_num = 100
# 
# def work_1():
#     global g_num
#     for i in range(3):
#         g_num += 1
#     print(f"子线程1中计算得出的值为: {g_num}")
# 
# def work_2():
#     global g_num
#     g_num += 1
#     print(f'子线程2中获取到的值为: {g_num}')
# 
# if __name__ == '__main__':
#     print(f'子线程未启动之前主线程获取的值为: {g_num}')  # 子线程启动之前获取全局变量
#     t1 = threading.Thread(target=work_1)
#     t1.start()
#     # time.sleep(0.1)  # 通过延迟保证t1线程中的任务执行完成,但推荐用下一行的代码
#     t1.join()  # 子线程对象调用join(timeout=None)方法,主线程会等待这个子线程绑定的任务执行完成,才会继续往下执行
# 
#     t2 = threading.Thread(target=work_2)
#     t2.start()
#     t2.join()
#     print(f'主线程最终获取到的值为: {g_num}')



"""
序列类型共享,不需要声明global
"""
# import threading
# g_nums = [11, 22, 33]
#
# def work_1(nums):
#     nums.append(44)
#     print(f'子线程1添加完成之后的列表为: {nums}')
#
# def work_2(nums):
#     print(f'子线程2获取到的列表为: {nums}')
#
# if __name__ == '__main__':
#     print(f'原列表为: {g_nums}')
#     t1 = threading.Thread(target=work_1, args=(g_nums,))
#     t1.start()
#     t1.join()
#     t2 = threading.Thread(target=work_2, args=(g_nums,))
#     t2.start()
#     t2.join()
#     print(f'主线程获取到的全局列表为: {g_nums}')



# ----------------------------多线程共享全局变量可能遇到的问题-----------------------------
"""
问题:
如果多个线程同时对同一个全局变量操作,会出现资源竞争问题,从而导致数据结果不正确:线程之间的切换并不是等某个线程的整个任务执行完毕才切换

例如:两个线程t1和t2都要对全局变量g_num(默认是0)进行加1运算,t1和t2都各自对g_num加10次,g_num的最终的结果理应为20。但是由于是多线程同时操作,有可能出现下面情况:
1. 在g_num=0时,t1取得g_num=0。此时系统把t1调度为”sleeping”状态,把t2转换为”running”状态,t2也获得g_num=0
2. 然后t2对得到的值进行加1并赋给g_num,使得g_num=1
3. 然后系统又把t2调度为”sleeping”,把t1转为”running”。线程t1又把它之前得到的0加1后赋值给g_num。
4. 这样导致虽然t1和t2都对g_num加1,但结果仍然是g_num=1
"""
# import threading
# import time
# 
# g_num = 0
# 
# def work_1(num):
#     global g_num
#     for i in range(num):
#         g_num += 1
#     print(f'线程1计算的结果是:{g_num}')
# 
# def work_2(num):
#     global g_num
#     for i in range(num):
#         g_num += 1
#     print(f'线程2计算的将结果是:{g_num}')
# 
# if __name__ == '__main__':
#     print(f'线程创建之前g_num的值为:{g_num}')
#     t1 = threading.Thread(target=work_1, args=(1000000,))
#     t1.start()
# 
#     t2 = threading.Thread(target=work_2, args=(1000000,))
#     t2.start()
# 
#     while len(threading.enumerate()) != 1:  # 等待子线程任务执行完毕之后再执行主线程代码
#         time.sleep(1)
#     print(f'两个线程对同一个全局变量操作之后的g_num的值为:{g_num}')  # 输出结果不是2000000



"""
解决方案:互斥锁
锁的好处:
- 确保了某段关键代码只能由一个线程从头到尾完整地执行
锁的坏处:
- 阻止了多线程并发执行,包含锁的某段代码实际上只能以单线程模式执行,效率就大大地下降了
- 由于可以存在多个锁,不同的线程持有不同的锁,并试图获取对方持有的锁时,可能会造成死锁
"""
# import threading
# import time
#
# mutex = threading.Lock()  # 创建一个互斥锁对象,默认是未上锁的状态
# g_num = 0
#
# def work_1(num):
#     global g_num
#     for i in range(num):
#         mutex.acquire()  # 上锁
#         g_num += 1
#         mutex.release()  # 解锁
#     print(f'线程1计算的结果是:{g_num}')
#
# def work_2(num):
#     global g_num
#     for i in range(num):
#         mutex.acquire()
#         g_num += 1
#         mutex.release()
#     print(f'线程2计算的将结果是:{g_num}')
#
# if __name__ == '__main__':
#     print(f'线程创建之前g_num的值为:{g_num}')
#     t1 = threading.Thread(target=work_1, args=(1000000,))
#     t1.start()
#
#     t2 = threading.Thread(target=work_2, args=(1000000,))
#     t2.start()
#
#     while len(threading.enumerate()) != 1:
#         time.sleep(1)
#     print(f'两个线程对同一个全局变量操作之后的g_num的值为:{g_num}')  # 输出结果是2000000



"""
死锁
"""
import threading
import time

mutexA = threading.Lock()
mutexB = threading.Lock()

class MyThread1(threading.Thread):
    def run(self):
        # 对mutexA上锁
        mutexA.acquire()

        # mutexA上锁后,延时1秒,等待另外那个线程把mutexB上锁
        print(self.name + '----do1---up----')
        time.sleep(1)

        # 此时会堵塞,因为这个mutexB已经被另外的线程抢先上锁了
        mutexB.acquire()
        print(self.name + '----do1---down----')
        mutexB.release()

        # 对mutexA解锁
        mutexA.release()

class MyThread2(threading.Thread):
    def run(self):
        # 对mutexB上锁
        mutexB.acquire()

        # mutexB上锁后,延时1秒,等待另外那个线程把mutexA上锁
        print(self.name + '----do2---up----')
        time.sleep(1)

        # 此时会堵塞,因为这个mutexA已经被另外的线程抢先上锁了
        mutexA.acquire()
        print(self.name + '----do2---down----')
        mutexA.release()

        # 对mutexB解锁
        mutexB.release()

if __name__ == '__main__':
    t1 = MyThread1()
    t2 = MyThread2()
    t1.start()
    t2.start()
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值