python3之多线程、共享变量、死锁问题

1、环境

-xubuntu、anaconda、pycharm、python3.7

-https://www.cnblogs.com/jokerbj/p/7460260.html

-https://www.dabeaz.com/python/UnderstandingGIL.pdf

2、多线程

-程序:一堆代码以文本形式存入一个文档

-进程:程序运行的一个状态

       -包含地址空间、内存数据线等

       -每个进程有自己完全独立的运行环境,多进程共享数据是一个问题

-线程

       -一个进程的独立运行片段,一个进程可以有多个线程

       -轻量化的进程

       -一个进程的多个线程间共享数据和上下文运行环境

       -共享互斥问题

-全局解释器锁(GIL)

        -Python代码的执行是由python虚拟机进行控制

        -在主循环中只能有一个控制线程在执行

-Python包

         -thread:有问题,不好用,python3改成了_thread

         -threading:通行的包

         -案例:第一个:顺序执行,耗时长;

                     第二个:多线程,耗时短

                     第三个:多线程,传参数

'''
利用time函数,生成两个函数
程序调用
计算总的运行时间
'''
import time
def loop1():
    #ctime 得到当前时间
    print('Start loop1 at:',time.ctime())
    #睡眠多长时间,单位是秒
    time.sleep(4)
    print('End loop1 at:',time.ctime())

def loop2():
    #ctime 得到当前时间
    print('Start loop2 at:',time.ctime())
    #睡眠多长时间,单位是秒
    time.sleep(2)
    print('End loop2 at:',time.ctime())

def main():
    print("Sarting at:",time.ctime())
    loop1()
    loop2()
    print("All done at:",time.ctime())

if __name__=='__main__':
    main()

运行:
Sarting at: Sun Mar 31 10:26:00 2019
Start loop1 at: Sun Mar 31 10:26:00 2019
End loop1 at: Sun Mar 31 10:26:04 2019
Start loop2 at: Sun Mar 31 10:26:04 2019
End loop2 at: Sun Mar 31 10:26:06 2019
All done at: Sun Mar 31 10:26:06 2019
-----------------------------------------------------------
'''
多线程
'''
import time
import _thread as thread
def loop1():
    #ctime 得到当前时间
    print('Start loop1 at:',time.ctime())
    #睡眠多长时间,单位是秒
    time.sleep(4)
    print('End loop1 at:',time.ctime())

def loop2():
    #ctime 得到当前时间
    print('Start loop2 at:',time.ctime())
    #睡眠多长时间,单位是秒
    time.sleep(2)
    print('End loop2 at:',time.ctime())

#有三个线程,一个主线程(主管),两个工作的人,干的活是loop1、loop2
def main():
    print("Sarting at:",time.ctime())
    #启动多线程的意思是用多线程去执行某个函数
    #启动多线程函数为start_new_thread
    #参数两个,一个是需要运行的函数名,第二个函数参数作为元组使用
    #注意:如果函数只有一个参数,需要参数后有一个括号,若有参数放在括号里
    thread.start_new_thread(loop1,())
    thread.start_new_thread(loop2,())
    print("All done at:",time.ctime())

if __name__=='__main__':
    main()
    #死循环,主线程一直等
    while True:
        time.sleep(1)
运行:
Sarting at: Sun Mar 31 10:54:45 2019
All done at: Sun Mar 31 10:54:45 2019
Start loop2 at:Start loop1 at:  Sun Mar 31 10:54:45 2019Sun Mar 31 10:54:45 2019

End loop2 at: Sun Mar 31 10:54:47 2019
End loop1 at: Sun Mar 31 10:54:49 2019
-----------------------------------------------------------------------------------
'''
利用time延时函数生成两个函数
利用多线程调用
计算总运行时间
练习带参数的多线程启动方法
'''
import time
#导入多线程包并更名为thread
import _thread as thread

def loop1(in1):
    print('Start loop1 at:',time.ctime())
    #把参数打印出来
    print("我是参数",in1)
    #睡眠多长时间,单位是秒
    time.sleep(4)
    print('End loop1 at : ',time.ctime())

def loop2(in1,in2):
    print('Start loop2 at:',time.ctime())
    #把参数打印出来,代表使用
    print("我是参数",in1,"和参数",in2)
    #睡眠多长时间,单位是秒
    time.sleep(2)
    print('End loop1 at : ',time.ctime())

def main():
    print("Sarting at:",time.ctime())

    thread.start_new_thread(loop1,("haha",))
    thread.start_new_thread(loop2,("kaka","lala"))
    print("All done at:",time.ctime())

if __name__=='__main__':
    main()
    #一定要有while语句
    #因为启动多线程后本程序就被作为主线程存在
    #如果主线程执行完毕,则子线程可能也需要终止
    while True:
        time.sleep(10)
运行:
Sarting at: Sun Mar 31 11:16:04 2019
All done at: Sun Mar 31 11:16:04 2019
Start loop2 at:Start loop1 at:  Sun Mar 31 11:16:04 2019Sun Mar 31 11:16:04 2019

我是参数我是参数  kakahaha 和参数 
lala
End loop1 at :  Sun Mar 31 11:16:06 2019
End loop1 at :  Sun Mar 31 11:16:08 2019

-threading的使用

      -直接利用threading.Thread生成Thread实例

              - t = threading.Thread(target=xxx,args=(xxx,))

              - t.start():启动多线程

              -t.join():等待多线程执行完成


import time
import threading
def loop1(in1):
    print('Start loop1 at:',time.ctime())
    #把参数打印出来
    print("我是参数",in1)
    #睡眠多长时间,单位是秒
    time.sleep(4)
    print('End loop1 at : ',time.ctime())

def loop2(in1,in2):
    print('Start loop2 at:',time.ctime())
    print("我是参数",in1,"和参数",in2)
    time.sleep(2)
    print('End loop1 at : ',time.ctime())

def main():
    print("Sarting at:",time.ctime())
    #生成threading.Thread实例
    t1 = threading.Thread(target=loop1,args=("haha",))
    t1.start()
    t2 = threading.Thread(target=loop2,args=("kaka","lala"))
    t2.start()
#加入join,让“All done at垫底”
    t1.join()
    t2.join()

    print("All done at:",time.ctime())

if __name__=='__main__':
    main()
    while True:
        time.sleep(10)

运行结果:
Sarting at: Sun Mar 31 16:33:06 2019
Start loop1 at: Sun Mar 31 16:33:06 2019
我是参数 haha
Start loop2 at: Sun Mar 31 16:33:06 2019
我是参数 kaka 和参数 lala
End loop1 at :  Sun Mar 31 16:33:08 2019
End loop1 at :  Sun Mar 31 16:33:10 2019
All done at: Sun Mar 31 16:33:10 2019

      -守护线程  daemon  我要和主线程共存亡!!!

              -如果在程序中将子线程设置成守护线程,则子线程会在主线程结束时自动退出

              -一般认为,守护线程不重要或者不允许离开主线程独立运行

              -守护线程案例能否有效果跟环境相关

              -案例:非守护/守护

#非守护
import time
import threading

def fun():
    print("Start fun")
    time.sleep(2)
    print("end fun")

print("Main thread")

t1 = threading.Thread(target=fun,args=())
t1.start()

time.sleep(1)
print("Main thread end")

运行结果:
Main thread
Start fun
Main thread end
end fun
------------------------------------------------------------------
#守护
import time
import threading

def fun():
    print("Start fun")
    time.sleep(2)
    print("end fun")

print("Main thread")
t1 = threading.Thread(target=fun,args=())

#设置守护线程的方法,必须在start之前设置,否则无效
#t1.deamon = True
t1.setDaemon(True)
t1.start()

time.sleep(1)
print("Main thread end")
运行结果:
Main thread
Start fun
Main thread end

-线程常用属性

        -threading.currentThread:返回当前线程变量

        -threading.enumerate:返回一个包含正在运行的线程的list,正在运行的线程指线程启动后、结束前

        -threading.activeCount:返回正在运行的线程数量,效果跟len(tghreading.enumerate)相同

        -thr.setName:给线程设置名字

        -thr.getName:得到线程的名字

import time,threading
#导入多线程包并更名为thread
import _thread as thread

def loop1():
    print('Start loop1 at:',time.ctime())
    #睡眠多长时间,单位是秒
    time.sleep(3)
    print('End loop1 at : ',time.ctime())

def loop2():
    print('Start loop2 at:',time.ctime())
    time.sleep(2)
    print('End loop1 at : ',time.ctime())

def loop3():
    print('Start loop3 at:', time.ctime())
    time.sleep(5)
    print('End loop3 at : ', time.ctime())

def main():
    print("Starting at: ",time.ctime())
    t1 = threading.Thread(target=loop1,args=())
    t1.setName("Thr_1")
    t1.start()

    t2 = threading.Thread(target=loop2, args=())
    t2.setName("Thr_2")
    t2.start()

    t3 = threading.Thread(target=loop3, args=())
    t3.setName("Thr_3")
    t3.start()

    #预计3秒后thread2已经自动结束
    time.sleep(3)
    #enumerate 得到正在运行子线程,即子线程1和子线程3
    for thr in threading.enumerate():
        #getName能够得到线程的名字
        print("正在运行的线程名字是:{0}".format(thr.getName()))

    print("正在运行的子线程数量为:{0}".format(threading.activeCount()))
    print("All done at:",time.ctime())

if __name__=="__main__":
    main()
    while True:
        time.sleep(10)
运行结果:
Starting at:  Sun Mar 31 17:35:38 2019
Start loop1 at: Sun Mar 31 17:35:38 2019
Start loop2 at: Sun Mar 31 17:35:38 2019
Start loop3 at: Sun Mar 31 17:35:38 2019
End loop1 at :  Sun Mar 31 17:35:40 2019
正在运行的线程名字是:MainThread
正在运行的线程名字是:Thr_1
正在运行的线程名字是:Thr_3
正在运行的子线程数量为:3
All done at: Sun Mar 31 17:35:41 2019
End loop1 at :  Sun Mar 31 17:35:41 2019
End loop3 at :  Sun Mar 31 17:35:43 2019

-直接继承自threading.Thread

        -直接继承Thread,重写run函数,类实例可以直接运行

import time
import threading

#1、类需要继承自threading.Thread
class MyThread(threading.Thread):
    def __init__(self,arg):
        super(MyThread, self).__init__()
        self.arg= arg

    #2、必须重写run函数,run函数代表的是真正执行的功能
    def run(self):
        time.sleep(2)
        print("the args for this class is {0}".format(self,arg))

for i in range(5):
    t = MyThread(i)
    t.start()
    t.join()

print("Main thread is done!!!")

企业版喜欢这么写:

import time
import threading

loop = [4,2]
class ThreadFunc:
    def __init__(self,name):
        self.name = name

    def loop(self,nloop,nsec):
        '''
        :param nloop:
        :param nsec:
        :return:
        '''
        print('Start loop', nloop,'at', time.ctime())
        time.sleep(nsec)
        print("Done loop", nloop,'at', time.ctime())

def main():
    print("Starting at:",time.ctime())
    '''ThreadFunc('loop').loop 跟以下两个式子相等
       t = ThreadFunc('loop')
        t.loop
    '''
    #以下t1和t2定义方式相等
    t = ThreadFunc("loop")
    t1 = threading.Thread(target=t.loop,args=("Loop1",4))
    #下面这种写法更西方人,工业化一点
    t2 = threading.Thread(target=ThreadFunc("loop").loop,args=("loop",2))

    #常见的错误写法
    #t1 = threadingThread(target=ThreadFunc("loop").loop(100,4))

    t1.start()
    t2.start()

    t1.join()
    t2.join()

    print("All done at: ",time.ctime())
if __name__ == '__main__':
    main()
运行:
Starting at: Mon Apr  1 09:48:54 2019
Start loop Loop1 at Mon Apr  1 09:48:54 2019
Start loop loop at Mon Apr  1 09:48:54 2019
Done loop loop at Mon Apr  1 09:48:56 2019
Done loop Loop1 at Mon Apr  1 09:48:58 2019
All done at:  Mon Apr  1 09:48:58 2019

3、共享变量

-共享变量:当多个线程同时访问一个变量的时候,会产生共享变量的问题

-解决变量:锁、信号灯

-(Lock):

       -是一个标志,表示一个线程在占用一些资源

       -使用方法:

               -上锁

               -使用共享资源,放心地用

               -取消锁,释放锁

               -锁谁:哪个资源需要多个线程共享,锁哪个,锁其实不是锁住谁,是一个令牌

-线程安全问题

         -如果一个资源/变量,他对于线程来讲,不用加锁也不会引起任何问题

         -线程不安全变量类型:list、set、dict

         -线程安全变量类型:queue

-生产者消费者问题

         -一个模型,可以用来搭建消息队列

         -queue是一个用来存放变量的数据结构,特点是先进先出,内部元素排队

import threading,time
#python2  from Queue import Queue
#python3
import queue

class Producer(threading.Thread):
    def run(self):
        global queue
        count = 0
        while True:
            #qsize返回queue内部长度
            if queue.qsize() < 1000:
                for i in range(100):
                    count += 1
                    msg = '生成产量'+str(count)
                    #put是往queue中放入
                    queue.put(msg)
                    print(msg)
            time.sleep(0.5)
class Consumer(threading.Thread):
    def run(self):
        global queue
        while True:
            if queue.qsize() > 100:
                for i in range(3):
                    #get从queue中取出一个值
                    msg = self.name + "消费了"+queue.get()
                    print(msg)
            time.sleep(1)
if __name__ == '__main__':
    queue = queue.Queue()
    for i in range(500):
        queue.put("初始产品"+str(i))
    for i in range(2):
        p = Producer()
        p.start()
    for i in range(5):
        c = Consumer()
        c.start()

-死锁问题

        -锁的等待时间问题

import threading,time
lock_1 = threading.Lock()
lock_2 = threading.Lock()

def func_1():
    print("func_1 starting...")
    lock_1.acquire(timeout=4)
    print("func_1 申请了lock_1...")
    time.sleep(2)
    print("lock_1等待lock_2")

    rst = lock_2.acquire(timeout=2)
    #申请锁是否成功
    if rst:
        print("func_1已经得到锁lock_2")
        lock_2.release()
        print("func_1释放了Lock_2...")
    else:
        print("func_1释放了lock_2")

    lock_1.release()
    print("func_1释放了lock_1")
    print("func_1 done...")

def func_2():
    print("func_2 starting...")
    lock_2.acquire()
    print("func_2 申请了lock_2...")
    time.sleep(4)
    print("lock_2等待lock_1")
    lock_1.acquire()
    print("func_2释放了Lock_1...")

    lock_1.release()
    print("func_2释放了lock_1")

    lock_2.release()
    print("func_2释放了lock_2")

    print("func_2 done...")

if __name__ == '__main__':
    print("主程序启动...")
    t1 = threading.Thread(target=func_1,args=())
    t2 = threading.Thread(target=func_2,args=())

    t1.start()
    t2.start()

    t1.join()
    t2.join()
    print("主程序启动...")
运行:
主程序启动...
func_1 starting...
func_1 申请了lock_1...
func_2 starting...
func_2 申请了lock_2...
lock_1等待lock_2
func_1释放了lock_2
func_1释放了lock_1
func_1 done...
lock_2等待lock_1
func_2释放了Lock_1...
func_2释放了lock_1
func_2释放了lock_2
func_2 done...
主程序启动...

-semaphore:允许一个资源最多由几个多线程同时使用

import threading,time
#参数定义最多几个线程同时使用资源
semaphore = threading.Semaphore(3)

def func():
    if semaphore.acquire():
        for i in range(5):
            print(threading.currentThread().getName() + "get semaphore")
        time.sleep(15)
        semaphore.release()
        print(threading.currentThread().getName() + "release semaphore")
for i in range(8):
    t1 = threading.Thread(target=func)
    t1.start()

-threading.Timer

import threading,time

def func():
    print(" I am running...")
    time.sleep(4)
    print("I am done....")

if __name__ == '__main__':
    #Timer的作用:六秒之后调用func
    t = threading.Timer(6,func)
    t.start()

    i = 0
    while True:
        print("{0}**************".format(i))
        time.sleep(3)
        i += 1

-可重入锁

        -一个锁,可以被一个线程多次申请

        -主要解决递归调用的时候,需要申请锁的情况

import threading,time

class MyThread(threading.Thread):
    def run(self):
        global num
        time.sleep(1)

        if mutex.acquire(1):
            num += 1
            msg = self.name+'set num to'+str(num)
            print(msg)
            mutex.acquire()
            mutex.release()
            mutex.release()

num = 0
#mutex = threading.Lock()
mutex = threading.RLock()


def testTh():
    for i in range(5):
        t = MyThread()
        t.start()
if __name__ == '__main__':
    testTh()
运行:
Thread-5set num to1
Thread-4set num to2
Thread-3set num to3
Thread-2set num to4
Thread-1set num to5

 

 

你可能感兴趣的:(Python知识)