第24课 多线程开发

一、多线程开发


1、进程和线程

  1、进程的概念:运行着的程序。比如,windows下的任务管理器,里面就是各种程序的进程。

  2、线程的概念:

    1---每个进程里面至少包含一个线程

    2---线程是操作系统创建的,用来控制代码执行的数据结构

    3---线程就像代码执行的许可证

    4---单线程程序,主线程的入口就是代码的开头

    5---主线程顺序往下执行,直到所有的代码都执行完

2、概念对应:通过银行办理业务来解释程序运行的相关概念

  1、一个服务窗口 ==CPU的一个核

  2、客户 == 进程(运行着的程序)

  3、调度员 == 操作系统

  4、服务号 == 线程

  5、调度员分配服务号给客户 == OS分配线程给进程代码

  6、服务窗口给客户办理业务 == CPU核心执行线程代码

3、线程库:

  1、代码通过系统调用,请求OS分配一个新的线程

  2、python里面,thread和threading都可以用来创建和管理线程

  3、thread比较底层---thread只有在python2中使用,python3已停用

  4、threading是thread模块的扩展,提供了很多线程同步功能,使用起来更加方便强大

4、我们为什么需要多线程 :

   1、多线程给一个程序并并行执行代码的能力----同时处理多个任务

    $convert

    >>convert 1.avi

    >>convert 2.avi

  2、常见的

    1、UI线程

    2、任务线程 task  exeute

5、多线程使用共享数据

  1、共享对象的概念,比如常见的自动取款机

    1---某个时刻只能一个人使用

    2---进入后往往会锁上门(表示已经被使用)

    3---看到里面有人,外面的人排队等待

    4---用完后开锁(表示已经取钱结束)

    5---后一个排队的人进去取钱(重复这个过程)

  2、共享数据使用场景实例

    1---YY用户支付宝账号的余额为2000元

    2---他乘坐滴滴打车要扣钱

    3---他的余额宝会给他挣钱

    4---处理滴滴打车的逻辑在线程#1里执行

    5---处理余额宝挣钱的逻辑在线程#2里执行

    6---今天,他坐滴滴打车扣了10块钱,假设余额宝挣的钱也是10块

  3、共享数据需要用线程锁,否则可能导致数据出错。

6、条件变量-----比较难懂,工作中遇到再研究吧

  

二、知识点补充

1、if __name__ == __'main__' 的使用

  1、有时候我们导入一个模块后,只是想使用这个模块的特定函数功能,并不想执行这个模块里面的测试代码,这时候可以使用if  __name__ ==  "__main__"方法

实例 :m1里面有测试代码,m2调用m1模块时,并不想执行m1里面的测试代码,这时候该怎么办?

#  m1模块

def sayHello():
    print('hello')

def sayGoodbye():
    print('goodbye')

# 测试代码  testing code

print('exec testing')
sayHello()
sayGoodbye()

m2调用m1模块,m1模块内的测试代码也被执行

# m2调用m1模块

import m1

m1.sayHello()


# 执行结果:

D:Pythonpython.exe "D:/Programs/HelloWorld2/songqin/python/lesson57 多线程/m2.py"
exec testing
hello
goodbye
hello

Process finished with exit code 0

m3中,入口模块是__main__,则执行m3中的测试代码。

def sayHello():
    print('hello')

def sayGoodbye():
    print('goodbye')
    
print(f'm3 name is ' + __name__)   # 打印m3的入口代码

if __name__ == '__main__':  # 如果入口代码是__main__,则执行测试代码
    print('exec testing')
    sayHello()
    sayGoodbye()


# 执行结果

D:Pythonpython.exe "D:/Programs/HelloWorld2/songqin/python/lesson57 多线程/m3.py"
m3 name is __main__
exec testing
hello
goodbye

Process finished with exit code 0

m4导入m3模块,这时候执行m4时,先执行m3里面的打印语句:print(f  'm3 name is'  +  __name__),因为m3是被引用的,这时候它的入口函数在m4里面已经变成了m3,

所以,m3里面的测试代码就不会被执行。然后再执行print(__name__),最后执行m3.sayHello()。

# m4

import m3   # 导入m3模块,这时候执行m3里面的print(f'm3 name is ' + __name__)语句,其它函数不调用不执行

print(__name__)  #打印m4的入口函数名称
 
m3.sayHello()  # 调用m3的sayHello函数


# 执行结果
D:Pythonpython.exe "D:/Programs/HelloWorld2/songqin/python/lesson57 多线程/m4.py"
m3 name is m3
__main__
hello

Process finished with exit code 0

实例

3-1     t1 = threading.Thread(target = thread1_entry)  ,threading是python的一个库,Thread是threading里面的类。

     target 后面跟的是函数对象,不是函数里面的返回值,所以不能写成target = thread1_entry(), thread1_entry称为入口函数

     t1只是创建了一个线程,但是并未开始执行,需要执行t1.start(),才会执行新的线程。就是执行入口函数thread1_entry里面的内容

 1 print('main thread start 1!')
 2 
 3 import threading
 4 from time import sleep
 5 
 6 def threda1_entry():
 7     print('child thread 1, start')
 8     sleep(15)
 9     print('child thread 1,  end')
10 
11 t1 = threading.Thread(target = thread1_entry)   # target:对象的意思,后面跟的是函数对象,不是对象的返回值   # Thread是threading模块的类
12 t1.start()
13 t1.sleep(10)
14 print(main thread end)
15 
16 
17 执行结果:
18 D:Pythonpython.exe "D:/Programs/HelloWorld2/songqin/python/lesson57 多线程/lesson57 知识点.py"
19 main thread start!
20 child thread 1, start
21 child thread 1, end
22 main thread end
23 
24 Process finished with exit code 0

4-1

 1 from time import sleep, ctime
 2 
 3 def thread1_entry(nsec):
 4     print('child thread 1, start at:', ctime())
 5     sleep(nsec)
 6     print('child thread 2, start at:', ctime())
 7 
 8 print('main thread start.')
 9 
10 def thread2_entry(nsec):
11     print('child thread 1, end.', ctime())
12     sleep(esec)
13     print('child ehread 2, end.',  ctime())
14 
15 # 创建线程对象,指定了新的入口函数
16 t1 = threading.Thread(target = thread1_entry, args = (1,))
17 t2 = threading.Thread(target = thread2_entry, args = (2,))
18 
19 # 启动新线程
20 t1.start()
21 t2.start()
22 
23 # t1线程结束
24 t1.join()
25 
26 # t2线程结束
27 t2.join()
28 print('main thread end.')
29 
30 
31 
32 # 执行结果
33 
34 D:Pythonpython.exe "D:/Programs/HelloWorld2/songqin/python/lesson57 多线程/lesson57 知识点.py"
35 main thread start.
36 child thread 1, start at: Fri Jul 10 15:30:22 2020
37 child thread 2, start at: Fri Jul 10 15:30:22 2020
38 child thread 1, end at: Fri Jul 10 15:30:23 2020
39 child thread 2, end at: Fri Jul 10 15:30:24 2020
40 main thread end.
41 
42 Process finished with exit code 0

5-1 多线程使用共享数据

 1 import threading
 2 from time import sleep
 3 
 4 def thread_entry():
 5 
 6     # 注意,局部变量var的值,同时被两个线程调用,会搞混乱吗?
 7     var = 1
 8     for i in range(10):
 9         # threading.currentThread().ident  获取当前线程的ID号
10         print('th #{}:{}'.format(threading.currentThread().ident, var))
11         sleep(1)
12         var += 1
13 
14 
15 print('main thread start.')
16 
17 t1 = threading.Thread(target=thread_entry)
18 t2 = threading.Thread(target=thread_entry)
19 
20 
21 t1.start()
22 t2.start()
23 
24 t1.join()
25 t2.join()
26 
27 print('main thread end.')
28 
29 
30 # 执行结果:
31 D:Pythonpython.exe "D:/Programs/HelloWorld2/songqin/python/lesson57 多线程/lesson57 知识点.py"
32 main thread start.
33 th #8788:1
34 th #7524:1
35 th #8788:2th #7524:2
36 
37 th #7524:3th #8788:3
38 
39 th #8788:4th #7524:4
40 
41 th #7524:5
42 th #8788:5
43 th #8788:6th #7524:6
44 
45 th #8788:7
46 th #7524:7
47 th #7524:8
48 th #8788:8
49 th #8788:9
50 th #7524:9
51 th #8788:10th #7524:10
52 
53 main thread end.
54 
55 Process finished with exit code 0

5-2-2   共享数据出现的问题,结果跟预期不符

import threading
from time import sleep

zhifubao = {
    'YY' : 2000,
    'dabao' : 50000,
    'liming' : 6005000
    }

# 线程1 :滴滴打车处理,参数是用户账户扣款金额

def thread1_didi_pay(account, amount):
    print('t1: get balance from bank')
    balance = zhifubao[account]


    # 下面的sleep(2)表示一些处理过程需要花上2秒钟
    print('* t1: do something(like discount lookup) for 2 second')
    sleep(2)

    print('* t1: deduct')
    zhifubao[account] = balance - amount


# 线程2:余额宝处理,参数是用户账户和当前利息
def thread2_yuebao_interest(account, amount):
    print('$ t2: get balance from bank')
    balance = zhifubao[account]

    # 下面的sleep(1)表示一些处理过程需要花上1秒钟
    print('$ t2: do something2 ...for 1 seconds')
    sleep(1)

    print('$ t2: add')
    zhifubao[account] = balance + amount



t1 = threading.Thread(target= thread1_didi_pay, args=('YY', 10))
t2 = threading.Thread(target= thread2_yuebao_interest, args=('YY', 10))


t1.start()
t2.start()
t1.join()
t2.join()

print('finally, YY balance is %s' % zhifubao['YY'])



执行结果:

D:Pythonpython.exe "D:/Programs/HelloWorld2/songqin/python/lesson57 多线程/lesson57 知识点.py"
t1: get balance from bank
* t1: do something(like discount lookup) for 2 second
$ t2: get balance from bank
$ t2: do something2 ...for 1 seconds
$ t2: add
* t1: deduct
finally, YY balance is 1990

Process finished with exit code 0

5-3-1  使用线程锁,避免共享数据出错

 1 import threading
 2 from time import sleep
 3 
 4 
 5 zhifubao = {
 6     'YY' : 2000,
 7     'lming' : 365000,
 8     'zhaolei' : 6005000
 9     }
10 
11 
12 
13 # 调用一个lock函数,返回一个锁对象
14 zhifubao_lock = threading.Lock()
15 
16 
17 def thread1_didi_pay(account, amount):
18     # 在代码访问共享对象之前,加锁
19     # 当多个线程同时执行lock.acquire时,只有一个线程能成功获取锁,然后继续执行代码
20     # 其他线程继续等待,直到获得锁为止
21     zhifubao_lock.acquire()
22     print('* t1: get balance from bank')
23     balance = zhifubao[account]
24 
25     print('* t1: do something(like discount lookup) for 2 seconds')
26     sleep(2)
27 
28 
29     print('*t1: deduct')
30     zhifubao[account] = balance - amount
31 
32 
33     # 访问完共享对象后,释放锁
34     # 访问结束后,一定要调用lock对象的acquire方法,进行解锁操作
35     # 否则其它等待锁的线程将永远等待下去,成为死线程
36     zhifubao_lock.release()
37 
38 
39 def thread2_yuebao_interest(account, amount):
40     # 在代码访问共享对象之前,加锁
41     zhifubao_lock.acquire()
42     print('$ t2: get balance from bank')
43     balance = zhifubao[account]
44 
45     print('$ t2: do something2...for 1 seconds')
46     sleep(1)
47 
48     print('$ t2: add')
49     zhifubao[account] = balance + amount
50     zhifubao_lock.release()
51 
52 t1 = threading.Thread(target=thread1_didi_pay, args=('YY', 2000))
53 t2 = threading.Thread(target=thread2_yuebao_interest, args=('YY', 2000))
54 
55 t1.start()
56 t2.start()
57 t1.join()
58 t2.join()
59 
60 print('finally, YY balance is %s:' % zhifubao['YY'])
61 
62 
63 
64 # 执行结果
65 
66 D:Pythonpython.exe "D:/Programs/HelloWorld2/songqin/python/lesson57 多线程/lesson57 知识点.py"
67 * t1: get balance from bank
68 * t1: do something(like discount lookup) for 2 seconds
69 *t1: deduct
70 $ t2: get balance from bank
71 $ t2: do something2...for 1 seconds
72 $ t2: add
73 finally, YY balance is 2000:
74 
75 Process finished with exit code 0
原文地址:https://www.cnblogs.com/nick1998/p/13270141.html