网络编程---第十一课:线程,共享资源,锁
参考连接:https://blog.csdn.net/qq_37615098/article/details/83933614
## 线程的概念
**进程的概念:运行着的程序
每个进程里面至少有一个线程
线程是操作系统创建的,用力啊控制代码执行的数据结构
线程就像代码的执行许可证**
**单线程程序。主线程的入口就是代码的开头
主线程顺序往下执行,直到所有的代码都执行完**
**
## 概念对应(银行里面办理业务)
**
***一个服务窗口 = CPU的一个核
客户 = 进程(运行着的程序)
调度员 = 操作系统(OS)
服务号 = 线程
调度员分配服务号给客户 = OS分配线程给进程代码
服务窗口给客户办理业务 = CPU核心执行线程代码***
## 调度的概念
调度员分配窗口给客户
一个客户不一定占用一个窗口直到它结束
比如需要很长时间填写表格
这是可以让下一个客户办理
先前的客户填好了表格,在继续
操作系统不会让一个线程一直占用CPU的
## 进程里的多线程
**线程库**
***代码通过系统调用,请求os分配一个新的线程
python里面使用封装好的库,python3只有threading,是标准库里面的
thread
threading
都可以用来创建和管理线程
thread比较底层
threading是thread模块的扩展,提供了很多线程同步功能,使用起来更加方便强大***
例子:
print("main thread start.")
import threading
from time import sleep
def thread1_entry():
print("child thread 1,start")
sleep(15)
print("child thread 1,end")
t1 = threading.Thread(target=thread1_entry) #Thread是一个类Thread()是实例化一个类,传入初始化方法的参数,target传的是这个函数的名字,不是调用。不加括号是函数的对象,target指定了线程对象的入口函数。函数的名字,并没有加括号,意味着target传入的值是一个函数对象,如果加(),target就变成了函数调用的返回值。执行起来的时候,入口函数就是thread1_entry,t1只是对应一个thread模块,要想执行要调用thread的start方法。新的线程执行入口函数里面的代码。
t1.start() #t1是Thread的实例对象,从这个点开始就启动了一个新的线程
sleep(10) #主线程要sleep10秒 主线程先结束
print("main thread end.")
**#结果**
main thread start.
child thread 1,start
main thread end.
child thread 1,end
***有些场景是:主线程要等待子线程执行完,比如子线程爬去数据,主线程分析数据、
要等到子线程结束(线程的.join()方法)***
import threading
from time import sleep,ctime,time
def thread1_entry(nsec):
print("child thread 1,start at:",ctime())
sleep(nsec)
print("child thread 1,end at:",ctime())
def thread2_entry(nsec):
print("child thread 2,start at:",ctime())
sleep(nsec)
print("child thread 2,end at:",ctime())
print("main thread start.")
#创建线程对象,指定了新线程的入口函数,args是传入入口函数的参数
t1 = threading.Thread(target=thread1_entry,args=(1,)) #Thread是一个类Thread()是实例化一个类,传入初始化方法的参数
t2 = threading.Thread(target=thread2_entry,args=(2,)) #(1,)元组,args=(1,)是入口函数的参数,就是用args传给他,只能传元组,一个元素的时候,一定要加逗号,这里是传入的是时间
#启动新线程
t1.start() #两个线程的实例化。调用start方法
t2.start()
start = time() #验证json等待的时间
#主线程等待t1线程结束,
t1.join()
#主线程等待t2线程结束
t2.join()
end = time() #验证json等待的时间
print(end-start) #验证json等待的时间
print("main thread end.")
#结果:
# main thread start.
# child thread 1,start at: Sat Nov 10 18:06:26 2018
# child thread 2,start at: Sat Nov 10 18:06:26 2018
# child thread 1,end at: Sat Nov 10 18:06:27 2018
# child thread 2,end at: Sat Nov 10 18:06:28 2018
# 2.0
# main thread end.
多线程使用局部变量不会乱套, 多线程使用全局变量会乱套
## 多线程使用共享数据
## 共享对象的概念
**从例子说起
高铁上的测试
某个时刻只能一个人使用
进入后往往立刻锁门(表示已经被使用)
看到的人,门口排队等待
用完开锁(表示已经使用完了)
排队的人中下一个去使用(重复这个过程)**
**有些资源是某个时刻时刻独占使用的
如果不加锁
某人使用厕所
另一个人也进入使用
发生冲突
锁保证了
只有一个人去使用
别人必须等待**
import threading
from time import sleep,ctime,time
#存储支付宝账号余额
zhifubao = {
"jcy":100000,
"liming":5000,
"wangmin":15000,
"zhaolei":6500000,
}
#线程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 seconds")
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 2 seconds")
sleep(1)
print("$ t2: add")
zhifubao[account] = balance + amount ##余额每天的收益
t1 = threading.Thread(target=thread1_didi_pay,args=("jcy",10)) #创立两个线程对象
t2 = threading.Thread(target=thread2_yuebao_interest,args=("jcy",10))
t1.start()
t2.start()
t1.join()
t2.join()
print("finally,jcy balance is %s"%zhifubao["jcy"])
#结果
# * t1:get balance from bank
# * t1:do something(like discount lookup) for 2 seconds
# $ t2:get balance from bank
# $ t2:do something2...... for 2 seconds
# $ t2: add
# * t1:deduct
# finally,jcy balance is 99990
**#应该是10万元**
#多个线程要访问共享数据的时候,特别是要修改数据的时候。我们可以使用**锁对象的机制**。同时只有一个线程处理他的时候,就不会有问题
import threading
from time import sleep,ctime,time
#存储支付宝账号余额
zhifubao = {
"jcy":100000,
"liming":5000,
"wangmin":15000,
"zhaolei":6500000,
}
#调用Lock函数,返回一个锁对象
zhifubao_lock = threading.Lock()
#线程1:滴滴打车处理,参数是用户账户和扣款金额
def thread1_didi_pay(account,amount):
#在代码访问共享对象之前,加锁
#当多个线程同时执行lock.acquire()时,
#只有一个线程能成功的获取锁,然后继续执行代码
#其他线程就继续等待,直到获得锁为止
zhifubao_lock.acquire()
print("* t1:get balance from bank")
balance = zhifubao[account] #通过账户把余额取出来,放到一个变量里面
#下面的sleep(2)表示一些处理过程中需要花上2秒钟
print("* t1:do something(like discount lookup) for 2 seconds")
sleep(2)
print("* t1:deduct")
zhifubao[account] = balance - amount #把余额扣掉
#访问共享对象释放锁
#访问结束后,一定要调用Lock对象的release方法,进行解锁操作
#否则其他等待锁的线程将永远等待下去
zhifubao_lock.release()
#线程2:余额宝处理,参数是用户账户和当前利息
def thread2_yuebao_interest(account, amount):
zhifubao_lock.acquire() #代码访问共享对象之前,加锁
print("$ t2:get balance from bank")
balance = zhifubao[account]
# 下面的sleep(1)表示一些处理过程中需要花上1秒钟
print("$ t2:do something2...... for 2 seconds")
sleep(1)
print("$ t2: add")
zhifubao[account] = balance + amount ##余额每天的收益
zhifubao_lock.release() #访问完共享对象 释放锁
t1 = threading.Thread(target=thread1_didi_pay,args=("jcy",10)) #创立两个线程对象
t2 = threading.Thread(target=thread2_yuebao_interest,args=("jcy",10))
t1.start()
t2.start()
t1.join()
t2.join()
print("finally,jcy balance is %s"%zhifubao["jcy"])
#结果
# * t1:get balance from bank
# * t1:do something(like discount lookup) for 2 seconds
# * t1:deduct
# $ t2:get balance from bank
# $ t2:do something2...... for 2 seconds
# $ t2: add
# finally,jcy balance is 100000
![在这里插入图片描述](https://img-blog.csdnimg.cn/20181110214842734.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzM3NjE1MDk4,size_16,color_FFFFFF,t_70)