我的第十三篇博客---python线程

多任务概念:
操作系统可以同时运行多个任务。
打个比方,你一边再用浏览器上网,一边听MP3,一边在用Word赶作业,这就是多任务
还有很多任务悄悄地在后台同时运行着,只是桌面上没有显示而已

现在,多核CPU已经非常普及了,但是,即使过去的单核CPU,也可以执行多任务。由于CPU执行代码都是顺序执行的,那么,单核CPU是怎么执行多任务的呢?
答案就是操作系统轮流让各个任务交替执行,任务1执行0.01秒,切换到任务2,任务2执行0.01秒,再切换到任务3,执行0.01秒……这样反复执行下去。表面上看,每个任务都是交替执行的,但是,由于CPU的执行速度实在是太快了,我们感觉就像所有任务都在同时执行一样。

真正的并行执行多任务只能在多核CPU上实现,但是,由于任务数量远远多于CPU的核心数量,所以,操作系统也会自动把很多任务轮流调度到每个核心上执行。

python中如何创建线程
python的标准库提供了两个模块:
_thread和threading。_thread是低级模块,threading是高级模块,对_thread进行了封装。
绝大多数情况下,我们只需要使用threading这个高级模块

启动一个线程就是把一个函数传入并创建Thread实例,然后调用start()开始执行:


示例代码:
import threading,time
def saysorry():
print('亲爱的,我错了')
time.sleep(2)
if __name__=='__main__':
for i in range(5):
t=threading.Thread(target=saysorry)
t.start() #启动线程,即让线程开始执行
结果是:同时打印出了5行‘亲爱的,我错了’ time.sleep(2) 并没有展示出相应的效果
说明同时执行了5个saysorry函数

几个需要注意的点
python中,默认情况下主线执行完自己的任务以后,就推出了,此时子线程会继续执行自己的任务,
直到自己的任务结束。如果需要主线程等待其他的子线程执行结束之后在终止,需要子线程调用join()函数
多线程程序的执行顺序是不确定的

示例代码:
import threading,time
def sing():
time.sleep(4)
print('正在唱歌')
def dance():
time.sleep(2)
print('正在跳舞')
if __name__=='__main__':
a1=threading.Thread(target=sing)
a2=threading.Thread(target=dance)
a1.start()
a2.start()
print('555')
结果是:先出现555,再出现‘正在跳舞’,最后出现‘正在唱歌’
因为是同时执行,有了sleep函数,他就延缓了 如果要按顺序出现 就需要join()函数了

import threading,time
def sing():
time.sleep(4)
print('正在唱歌')
def dance():
time.sleep(2)
print('正在跳舞')
if __name__=='__main__':
a1=threading.Thread(target=sing)
a2=threading.Thread(target=dance)
a1.start()
a1.join()
a2.start()
a2.join()
print('555')
这样的结果就是:
正在唱歌
正在跳舞
555

多线程-共享全局变量
多线程之间共享全局变量有2种方式:
1、子线程函数使用global关键字来调用函数外全局变量
2、列表当作实参传递到线程中

在一个进程内的所有线程共享全局变量,很方便在多个线程间共享数据

缺点就是,线程是对全局变量随意遂改可能造成多线程之间对全局变量的混乱(即线程非安全)

共享全局变量-global全局变量
from threading import Thread
import time
q=100
def work1():
global q
for i in range(3):
q+=1
print('经过work1,q的值为%d'%q)
def work2():
global q
for i in range(2):
q+=1
print('经过work2,q的值为%d'%q)
a=Thread(target=work1)
s=Thread(target=work2)
a.start()
a.join()
s.start()
s.join()
print(q)
结果为:
经过work1,q的值为103
经过work2,q的值为105
105

共享全局变量-列表当实参
from threading import Thread
def work1(a):
a.append(4)
print('经过work1,列表为%s'%a)
def work2(a):
a.append(5)
print('经过work2,列表为%s'%a)
w=[1,2,3]
q=Thread(target=work1,args=(w,))
s=Thread(target=work2,args=(w,))
q.start()
s.start()
print(w)
结果为:
经过work1,列表为[1, 2, 3, 4]
经过work2,列表为[1, 2, 3, 4, 5]
[1, 2, 3, 4, 5]

import threading,time
def a1(num):
global add
add+=num
print(add)
def a2(num):
global add
add+=num
print(add)
add=0
q1=threading.Thread(target=a1,args=(3,))
q2=threading.Thread(target=a2,args=(4,))
q1.start()
print('===============')
q2.start()
print('最终结果为:%d'%add)

结果为:
3
===============
7
最终结果为:7

同步的概念:
同步就是协同步调,按预定的先后次序进行运行。如:你说完,我再说

进程、线程同步:
可理解为进程或线程A和B一块配合,A执行到一定程度时要依靠B的某个结果,于是停下来,示意B执行;B执行,再将结果给A;A再继续操作。

并发:交替执行多任务
并行:同时执行多任务

import threading
print(threading.current_thread())
print(threading.enumerate())
查看有几个线程
print(len(threading.enumerate())

多线程程序的执行顺序是不确定的
多线程共享全局变量
互斥锁
死锁

互斥锁
(1、当多个线程几乎同时修改某一个共享数据的时候,需要进行同步控制

(2、线程同步能够保证多个线程安全访问竞争资源,最简单的同步机制是引入互斥锁

(3、互斥锁为资源引入一个状态: 锁定/非锁定

(4、某个线程要更改共享数据时,先将其锁定,此时资源的状态为‘锁定’,其他线程不能更改;直到该线程释放资源,将资源的状态变成‘非锁定’,其他的线程才能再次锁定该资源。互斥锁保证了每次只有一个线程进入写入操作,从而保证了多线程情况下的正确性


操作::

threading.Lock()
threading模块中定义了Lock类,可以方便的处理锁定:
#创建锁
mutex=threading.Lock()
#锁定
mutex.acquire()
#释放
mutex.release()

注意:
如果这个锁之前是没有上锁的,那么acquire不会堵塞
如果再调用acquire对这个锁上锁之前,他已经被其他线程上了锁,那么此时acquire会堵塞,直到这个锁被解锁为止

这个锁是当多个线程几乎同时修改某一个共享数据的时候,需要进行的同步控制
是修改某一个共享数据!!!!!!!!!!!!!!

示例代码:
import threading,time
def work1():
global num
c=0
mutex.acquire()
for x in range(4):
num+=1
c+=1
print('%d次的值为%d'%(x,num))
if c==2:
mutex.release()
time.sleep(2)
mutex.acquire()
mutex.release()
def work2():
global num
mutex.acquire()
num=num+1
print('经过work2后的值为:%d'%num)
mutex.release()
num=1
mutex=threading.Lock()
a=threading.Thread(target=work1)
s=threading.Thread(target=work2)
a.start()
s.start()
s.join()
a.join()
num=num+2
print(num)

结果是:
0次的值为2
1次的值为3
经过work2后的值为:4
2次的值为5
3次的值为6
8


锁的优缺点:
锁的优点:
确保了某段关键代码只能由一个线程从头到尾完整地执行

锁的缺点:
1、阻止了多线程并发执行,包含锁的某段代码实际上只能以单线程模式执行,效率就大大地下降了
2、由于可以存在多个锁,不同的线程持有不同的锁,并试图获取对方持有的锁时,可能会造成死锁

死锁:
在线程间共享多个资源的时候,如果两个线程分别占有一部分资源并且同时等待对方的资源,就会造成死锁。
尽管死锁很少发生,但一旦发生就会造成应用的停止响应

避免死锁
1、程序设计时要尽量避免(银行家算法)
2、添加超时时间等待

猜你喜欢

转载自www.cnblogs.com/sll-csdn/p/10925950.html