zl程序教程

您现在的位置是:首页 >  其他

当前栏目

python-thread多线程通俗理解

2023-03-14 09:51:15 时间

本文用简单的案例让读者理解 thread线程。

什么是线程:线程也叫轻量级进程,是操作系统能够进行运算调度的最小单位,它被包涵在进程之中,是进程中的实际运作单位。线程自己不拥有系统资源,只拥有在运行中必不可少的资源,但它可与同属一个进程的其他线程共享进程所拥有的全部资源。一个线程可以创建和撤销另一个线程,同一个进程中的多个线程之间可以并发执行。

thread机制:1、在python中,主线程结束后,会默认等待子线程结束后,主线程才退出。而在C/C++中,主线程结束后会把子线程kill掉。

      2、如在一个线程1中调用thread2.join(),则thread2结束后,线程1才会接着thread2.join()往后运行。

      3、主线程A启动了子线程B,调用b.setDaemaon(True),则主线程A结束时,会把子线程B也杀死。

      4、print(len(threading.enumerate()))  查看线程数量

 

案例一:炒菜与打乒乓球同步进行(主线程加一个子线程)

 1 import threading
 2 import time
 3 
 4 def func():
 5     for _ in range(5):
 6         print("线程1:打乒乓球")
 7         time.sleep(1)
 8 
 9 if __name__ == '__main__':
10     thread = threading.Thread(target=func)#创建子线程
11     thread.start()
12     for _ in range(5):
13         print('线程2:炒菜')#主线程和子线程轮流获得cpu资源
14         time.sleep(1)

案例二:炒股与打台球同步进行(主线程加两个子线程)

 1 import threading
 2 import time
 3 
 4 def test1():
 5     for i in range(5):
 6         print("炒股...%d"%i)
 7         time.sleep(1)
 8 
 9 def test2():
10     for i in range(5):
11         print("打台球...%d"%i)
12         time.sleep(1)
13         
14 t1 = threading.Thread(target=test1)
15 t2 = threading.Thread(target=test2)
16 t1.start()
17 t2.start()
18 #time.sleep(5)    #如果放开等待时间,“收货精彩人生”将在最后打印
19 print("收获精彩人生")

案例三:吵架与打擦边球同步进行(设置保护线程)

 1 import threading
 2 import time
 3 
 4 def test():
 5     for i in range(5):
 6         print("子线程吵架:", i)
 7         time.sleep(1)
 8 
 9 if __name__ == '__main__':
10     t1 = threading.Thread(target=test)
11     t1.setDaemon(True)# 设置线程保护
12     t1.start()
13     time.sleep(2)
14 
15     print("主线程打完擦边球后退出")
16     exit()

 # 如果没有设置线程保护,即使主线程exit退出,子线程也会继续运行

 案例四:生于忧患死于安乐(.join()指定线程优先执行)

 1 import threading
 2 #定义线程要调用的方法,*add可接收多个以非关键字方式传入的参数
 3 def action(*add):
 4     for arc in add:
 5         print(threading.current_thread().getName() +" "+ arc)#调用 getName() 方法获取当前执行该程序的线程名
 6 #定义为线程方法传入的参数
 7 my_tuple = ("苦其心志",\
 8             "劳其筋骨",\
 9             "饿其体肤",\
10             "空乏其身")
11 thread = threading.Thread(target = action,args =my_tuple)
12 thread.start()
13 thread.join()  #指定线程优先执行完毕,完毕后才继续往下执行。
14 
15 for i in range(5): #主线程执行如下语句
16     print(threading.current_thread().getName() + " 增益其所不能")

案例五:一千万次的反复无常(同步锁lock)

import threading

num = 0
def add():
    lock.acquire()#如果不加锁,频繁线程切换会导致两个线程数据不同步,最终num的值不是0.
    global num
    for i in range(10_000_000):
        num += 1
    lock.release()
def sub():
    lock.acquire()#如果不加锁,频繁线程切换会导致两个线程数据不同步,最终num的值不是0.
    global num
    for i in range(10_000_000):
        num -= 1
    lock.release()
    
if __name__ == "__main__":
    lock = threading.Lock()
    subThread01 = threading.Thread(target=add)
    subThread02 = threading.Thread(target=sub)
    subThread01.start()
    subThread02.start()
    subThread01.join()
    subThread02.join()
    print("num result : %s" % num) #num为0

对于同步锁来说,一次acquire()必须对应一次release(),不能出现连续重复使用多次acquire()后再重复使用多次release()的操作,这样会引起死锁造成程序的阻塞,完全不动了。

 案例六:千千万万万万千千个日夜(同步锁lock自加自解)

 1 import threading
 2 num = 0
 3 
 4 def add():
 5     with lock:
 6         # 自动加锁
 7         global num
 8         for i in range(10_000_000):
 9             num += 1
10         # 自动解锁
11 def sub():
12     with lock:
13         # 自动加锁
14         global num
15         for i in range(10_000_000):
16             num -= 1
17         # 自动解锁
18 
19 if __name__ == "__main__":
20     lock = threading.Lock() # threading.Lock()对象中实现了enter__()与__exit()方法;换成递归锁.RLock有相同效果
21     subThread01 = threading.Thread(target=add)
22     subThread02 = threading.Thread(target=sub)
23 
24     subThread01.start()
25     subThread02.start()
26     subThread01.join()
27     subThread02.join()
28     print("num result : %s" % num) # num返回0

 案例七:循序渐进,以免固步自封(递归锁RLock,避免死锁)

 1 import threading
 2 
 3 num = 0
 4 def add():
 5     lock.acquire()
 6     lock.acquire()#如果是同步锁,连续两次acquire会发生死锁现象
 7     global num
 8     for i in range(10_000_000):
 9         num += 1
10     lock.release()
11     lock.release()
12 def sub():
13     lock.acquire()
14     lock.acquire()
15     global num
16     for i in range(10_000_000):
17         num -= 1
18     lock.release()
19     lock.release()
20 
21 if __name__ == "__main__":
22     lock = threading.RLock()
23     subThread01 = threading.Thread(target=add)
24     subThread02 = threading.Thread(target=sub)
25     subThread01.start()
26     subThread02.start()
27     subThread01.join()
28     subThread02.join()
29 
30     print("num result : %s" % num)

总结多线程

优点:

1、进程之间不能共享内存,但线程之间共享内存非常容易;

2、操作系统在创建进程时,需要为该进程重新分配系统资源,但创建线程的代价则小得多。因此使用多线程来实现多任务并发;
缺点:

1、由于线程之间是进行随机调度,并且每个线程可能只执行n条执行之后,当多个线程同时修改同一条数据时可能会出现脏数据,所以出现了线程锁,即同一时刻允许一个线程执行操作。线程锁用于锁定资源,可以定义多个锁,当需要独占某一个资源时,任何一个锁都可以锁定这个资源,就好比你用不同的锁都可以把这个相同的门锁住一样。

2、如果有多个线程同时操作一个对象,如果没有很好地保护该对象,会造成程序结果的不可预期, 我们因此也称为“线程不安全”。

 

 

 一个人成功的速度取决于你从挫败中恢复的速度,你从挫败中恢复得越快,你成功的速度就会越快。你的认知水平和境界,与你所遭受的痛苦之间是有关系的,很多痛苦根本解决不了,唯一的办法是提高你的认知水平,认知水平一高,这些事情迎刃而解,它就不再是一个问题了。