Python高级编程——12.(2)系统编程之多线程

一、多线程与多进程的效率对比
(1)多线程
from threading import Thread,current_thread  #  多线程    current_thread 当前线程
import time
def run(msg):
        print("这个是独立线程运行的代码",msg)
        time.sleep(1)
if __name__ == '__main__':
    new_time = time.time()
    print("++++++++++  main start +++++++++++")
    for x in range(10):
        t1 = Thread(target=run,args=("这个是参数%s"%x,))
        t1.start()
    print("==========  main end ==========", current_thread().getName(),time.time()-new_time)
                                               # current_thread().getName()可以得到当前主线程的名称
'''                                             
运行结果:
++++++++++  main start +++++++++++
这个是独立线程运行的代码 这个是参数0
这个是独立线程运行的代码 这个是参数1
这个是独立线程运行的代码 这个是参数2
这个是独立线程运行的代码 这个是参数3
这个是独立线程运行的代码 这个是参数4
这个是独立线程运行的代码 这个是参数5
这个是独立线程运行的代码 这个是参数6
这个是独立线程运行的代码 这个是参数7
这个是独立线程运行的代码 这个是参数8
这个是独立线程运行的代码 这个是参数9
==========  main end ========== MainThread,0.0010006427764892578
'''
(2)多进程

from multiprocessing import Process   # 多进程
import time
def run(msg):
        print("这个是独立线程运行的代码",msg)
        time.sleep(1)
if __name__ == '__main__':
    new_time = time.time()
    print("++++++++++  main start +++++++++++")
    for x in range(10):
        t1 = Process(target=run,args=("这个是参数%s"%x,))
        t1.start()
    print("==========  main end ==========",time.time()-new_time)
'''
运行结果:
++++++++++  main start +++++++++++
==========  main end ========== 0.2971487045288086
这个是独立线程运行的代码 这个是参数0
这个是独立线程运行的代码 这个是参数2
这个是独立线程运行的代码 这个是参数1
这个是独立线程运行的代码 这个是参数9
这个是独立线程运行的代码 这个是参数3
这个是独立线程运行的代码 这个是参数5
这个是独立线程运行的代码 这个是参数8
这个是独立线程运行的代码 这个是参数4
这个是独立线程运行的代码 这个是参数7
这个是独立线程运行的代码 这个是参数6
'''
二、多线程,类的实现方式

from threading import Thread
class Team(Thread):
    def __init__(self, name):
        super().__init__(name=name)  # 继承自Thread

    def run(self):
        for i in range(3):
            print("这是类中的子线程")

if __name__ == '__main__':
    print("+++++++++++  main  start ++++++++++++")
    t = Team("丽丽")
    t.start()
    print("==========  main  end  ============")
'''
运行结果:
+++++++++++  main  start ++++++++++++
这是类中的子线程                                                           # 可以看出多线程的运行速度几乎超过了主线程,这在多进程中是不会出现的
==========  main  end  ============
这是类中的子线程
这是类中的子线程
这是类中的子线程
'''
 三、多线程共享全局变量

我们前面已经说过了,多进程之间的数据时独立的,各自都有一份,即便是全局变量也不共享,那么一个进程中的多线程之间的全局变量呢?注意:多线程之间的全局数据数是共享的,因为多线程是在一个进程,数据是互相可以访问的,案例如下:

import threading import time num = 100 def task1():     global num     for in range(3):         num += 1     print("run1中的num=",num) def task2():     print("run2中的num=",num) def run():     t1 = threading.Thread(target=task1)     t1.start()     time.sleep(1)     t2 = threading.Thread(target=task2)     t2.start() if __name__ == '__main__':     run()

由此可知,一个进程间的多线程(下面开始统一叫多线程,因为我们一直说的多线程就是一个进程下的多线程)的全局数据是共享的。

四、互斥锁(metux)

当多个线程⼏乎同时修改某⼀个共享数据的时候,需要进⾏同步控制线程同步能够保证多个线程安全访问竞争资源,最简单的同步机制是引⼊斥锁

互斥锁为资源引⼊⼀个状态:锁定/⾮锁定。

某个线程要更改共享数据时,先将其锁定,此时资源的状态为“锁定”,其他线程不能更改;直到该线程释放资源,将资源的状态变成“⾮锁定”,其他的线程才能再次锁定该资源。互斥锁保证了每次只有⼀个线程进⾏写⼊操作,从⽽保证了多线程情况下数据的正确性。

from threading import *

num = 0
def run1(lock):
    global num
    for x in range(1000000):
        lock.acquire( )  # 将程序锁定
        num += 1
        lock.release( ) # 解锁
    print(num)

def run2(lock):
    global num
    for x in range(1000000):
        lock.acquire()   # 将程序锁定
        num += 1
        lock.release()    # 解锁
    print(num)
if __name__ == '__main__':
    lock = Lock()  # 创建互斥锁,用于解决非线程安全问题
    t1 = Thread(target=run1,args=(lock,))
    t1.start()
    t2 = Thread(target=run2,args=(lock,))
    t2.start()

# 运行结果:1993755
#                  2000000
如果不加互斥锁,运行的结果应该会小于2000000,可参考12.系统编程(多进程和多线程)

五、同步(借助于互斥锁实现的)
from threading import Thread,Lock
import time
myLock1 = Lock()
myLock2 = Lock()
myLock3 = Lock()
# 通过加锁来实现一个同步案例,同步即多线程中的协同步调
def run1():
    while True:
        if myLock1.acquire():
            print("run1")
            myLock2.release()
            time.sleep(1)

def run2():
    while True:
        if myLock2.acquire():
            print("run2")
            myLock3.release()
            time.sleep(1)

def run3():
    while True:
        if myLock3.acquire():
            print("run3")
            myLock1.release()
            time.sleep(1)
if __name__ == '__main__':
    myLock2.acquire()
    myLock3.acquire()
    t1 = Thread(target=run1)
    t1.start()
    t3 = Thread(target=run3)
    t3.start()
    t2 = Thread(target=run2)
    t2.start()
# 运行结果:
# run1
#run2
# run3
# run1
#run2
#run3
......

六、死锁

在线程间共享多个资源的时候,如果两个线程分别占有⼀部分资源并且同时等待对的资源,就会造成死锁。

尽管死锁很少发⽣,但⼀旦发⽣就会造成应⽤的停⽌响应。

from threading import Thread,Lock
import  time

# 申请两个全局锁
myLock1= Lock()
myLock2= Lock()
def run1():
    print("第一个子线程运行 ")
    if myLock1.acquire(timeout=2):  #  等待2s
        print("lock1已经加锁了")
        time.sleep(1)  # 人为的代码停止一下
        if myLock2.acquire(): # 返回布尔值,表示加锁成功,第一次返回布尔值,第二次卡死
            print("第一个执行了吗?")
        myLock2.release()
    myLock1.release()
def run2():
   print("第二个子线程运行了")
   if myLock2.acquire():
       print("lock2已经加锁了")
       time.sleep(1)
       if myLock1.acquire():
           print("第二个执行了吗?")
       myLock1.release()
   myLock2.release()
if __name__ == '__main__':
    t1 = Thread(target=run1)
    t1.start()
    t2 = Thread(target=run2)
    t2.start()

# 运行结果:
#     第一个子线程运行
#     lock1已经加锁了
#     第二个子线程运行了
#     lock2已经加锁了
可以用递归加锁的方式解决死锁现象:rlock(),或者银行家算法

七、队列

八、Threadlocal

在多线程环境下,每个线程都有⾃⼰的数据。⼀个线程使⽤⾃⼰的局部变量⽐使⽤全局变量好,

因为局部变量只有线程⾃⼰能看见,不会影响其他线程,⽽全局变量的修改必须加锁。


from threading import Thread, local

mylocal = local()  # new一个local对象

def printMsg():
    print(mylocal.name, mylocal.age, mylocal.gender)

def speak():
    print("%s说了一句话" % mylocal.name)

def run1():
    # 自身函数中需要的参数绑定到本地线程Threadlocal    mylocal.name = "张三"
    mylocal.age = 18
    mylocal.gender = ""
    printMsg()
    speak()

def run2():
    mylocal.name = "李四"
    mylocal.age = 25
    mylocal.gender = ""
    printMsg()
    speak()

if __name__ == '__main__':
    t1 = Thread(target=run1)
    t2 = Thread(target=run2)
    t1.start()
    t2.start()
    #  运行结果:   张三 18     #             张三说了一句话
    #             李四 25     #             李四说了一句话

 
  
 
  













你可能感兴趣的:(基础篇)