zoukankan      html  css  js  c++  java
  • 初识多线程

     1 import threading
     2 import time
     3 def coding():
     4     for i in range(3):
     5         print('输入代码%s' %i)
     6         time.sleep(1)
     7 def drawing():
     8     for i in range(3):
     9         print('开始画画%s'%i)
    10         time.sleep(1)
    11 def main():
    12     t1 = threading.Thread(target=coding)
    13     t2 = threading.Thread(target=drawing)
    14 
    15     t1.start()
    16     t2.start()
    17 if __name__ == '__main__':
    18     main()
    19 
    20 Output:
    21 
    22 D:PyCharmAnaconda3python.exe D:/PyCharm/Py_work/test2.py
    23 输入代码0
    24 开始画画0
    25 输入代码1
    26 开始画画1
    27 输入代码2
    28 开始画画2
    29 
    30 Process finished with exit code 0

    还可以通过继承Thread类来创建多线程:

     1 import threading
     2 import time
     3 class codingThread(threading.Thread):
     4     def run(self):
     5         for i in range(3):
     6             print('输入代码%s' %threading.current_thread())
     7             time.sleep(1)
     8 class drawingThread(threading.Thread):
     9     def run(self):
    10         for i in range(3):
    11             print('开始画画%s'%threading.current_thread())
    12             time.sleep(1)
    13 def main():
    14     t1 = codingThread()
    15     t2 = drawingThread()
    16 
    17     t1.start()
    18     t2.start()
    19 if __name__ == '__main__':
    20     main()
    21 
    22 Output
    23 
    24 D:PyCharmAnaconda3python.exe D:/PyCharm/Py_work/test2.py
    25 输入代码<codingThread(Thread-1, started 8836)>
    26 开始画画<drawingThread(Thread-2, started 9080)>
    27 开始画画<drawingThread(Thread-2, started 9080)>
    28 输入代码<codingThread(Thread-1, started 8836)>
    29 输入代码<codingThread(Thread-1, started 8836)>
    30 开始画画<drawingThread(Thread-2, started 9080)>
    31 
    32 Process finished with exit code 0

    多线程共享全局变量的问题:

    多线程都是在同一个进程中运行的。因此在进程中的全局变量所有线程都是共享的。这就造成了一个问题,因为线程执行的顺序是无序的,有可能会造成数据错误。

     1 ###当数据量小的时候,可能不会出错,如下:
     2 import threading
     3 value = 0
     4 def add_value():
     5     global value
     6     for i in range(1000):
     7         value += 1
     8     print(value)
     9 def main():
    10     for x in range(2):
    11         t = threading.Thread(target=add_value)
    12         t.start()
    13 
    14 if __name__ == '__main__':
    15     main()
    16 
    17 Output
    18 
    19 1000
    20 2000

    第一个线程先加到1000,第二线程再加到2000,这里看起来没什么问题。但是当数据量大的时候,问题就出现了,如下:

     1 import threading
     2 value = 0
     3 def add_value():
     4     global value
     5     for i in range(1000000):
     6         value += 1
     7     print(value)
     8 def main():
     9     for x in range(2):
    10         t = threading.Thread(target=add_value)
    11         t.start()
    12 
    13 if __name__ == '__main__':
    14     main()
    15 
    16 Output
    17 
    18 1222047
    19 1246006

    按理说应该先输出1000000,再输出2000000。但是结果却不是,这就是多线程执行顺序无序的后果。举个例子:当value=10的时候,线程1和线程2同时启动,本来value经过两个线程执行后应该是value=12。但是由于两个线程是同时执行,导致value +=1 被同时执行了两次,最后的结果就是value还是等于11.

    这时候,就需要一个来消除这种不好的后果。就是如果线程1比线程2线对value先执行的时候,那么就先把value给锁起来,不让其他线程对value进行任何操作,等线程1操作完后再对value 进行解锁,这时候其他线程才可以对value进行操作。(这样看起来,好像和单线程没啥区别~~~~~)。

    注意:这个锁一般只用于多个线程修改全局变量的代码段。比如下面代码的7行和8行。

     1 import threading
     2 value = 0
     3 gLock = threading.Lock()
     4 def add_value():
     5     global value
     6     gLock.acquire()  #上锁
     7     for i in range(1000000):
     8         value += 1
     9     gLock.release()  #解锁
    10     print(value)
    11 def main():
    12     for x in range(2):
    13         t = threading.Thread(target=add_value)
    14         t.start()
    15 
    16 if __name__ == '__main__':
    17     main()
    18 
    19 Output
    20 
    21 1000000
    22 2000000

    Lock版本生产者和消费者模式

    生产者和消费者模式是多线程开发中经常见到的一种模式。生产者的线程专门用来生产一些数据,然后存放到一个中间变量中。消费者再从这个中间的变量中取出来数据进行消费。但是因为要使用中间变量,中间变量经常是一些全局变量,因此余姚使用锁来保证数据完整性。

     1 import threading,random,time
     2 
     3 gMoney = 1000 #此全局变量用来保存生产和消费的钱
     4 gLock = threading.Lock()
     5 gTotalTimes = 10  #此全局变量用来保存需要消费多少次
     6 gtimes = 0   #此全局变量用来保存生产了多少次
     7 
     8 class producer(threading.Thread):  #生产者
     9     def run(self):
    10         global gMoney,gtimes
    11         while True:
    12             money = random.randint(100,1000)
    13             gLock.acquire()     #上锁
    14             if gtimes >= gTotalTimes:  #如果生产了10次,就退出循环
    15                 gLock.release()
    16                 break
    17             gMoney += money
    18             print('%s生产了%d元钱,剩余%d元钱' %(threading.current_thread(),money,gMoney))
    19             gtimes += 1
    20             gLock.release()    #解锁
    21             time.sleep(0.5)
    22 
    23 class consumer(threading.Thread):  #消费者
    24     def run(self):
    25         global gMoney
    26         while True:
    27             money = random.randint(100,1000)
    28             gLock.acquire()
    29             if gMoney >= money:
    30                 gMoney -= money
    31                 print('%s消费了%d元钱,剩余%d元钱' % (threading.current_thread(), money, gMoney))
    32             else:
    33                 if gtimes >= gTotalTimes:
    34                     gLock.release()
    35                     break
    36                 print('%s消费者准备消费%d元钱,剩余%d元钱,余额不足' % (threading.current_thread(), money, gMoney))
    37 
    38             gLock.release()  #此处的解锁如果放在if代码块里面,可能会造成死锁
    39             time.sleep(0.5)
    40 
    41 def main():
    42     for i in range(5):
    43         t = consumer(name='消费者线程%d' %i)
    44         t.start()
    45 
    46     for i in range(5):
    47         t = producer(name='生产者线程%d' %i)
    48         t.start()
    49 
    50 if __name__ == '__main__':
    51     main()

    Condition版的生产者和消费者模式:

    Lock版本的生产者和消费者模式可以正常的运行。但是存在一个不足,在消费者中,总是通过while Ture死循环并且上锁的方式去判断钱够不够。上锁是一个很费CPU资源的行为,因此这种方式不是最好的。

    还有一种更好的方式便是使用threading.Condition来实现。threading.Condition可以在没有数据的时候处于阻塞等待状态。一旦有合适的数据了,还可以使用notify相关的函数来同时其他处于等待状态的线程。这样就可以不用做一些无用的上锁和解锁操作,可以提高程序的性能。threading.Condition和threading.Lock部分功能类似,其相关函数介绍如下:

    1、acquire:上锁

    2、release:解锁

    3、wait:将当前线程处于等待状态,并且会释放锁,可以被其他线程使用notify或者notify_all函数唤醒。被唤醒后会继续等待上锁,上锁后继续执行下面的代码。

    4、notify:通知某个正在等待的线程。默认是第1个等待的线程。

    5、notify_all:通知所有正在等待的线程。notify和notify_all不会释放锁,因此需要在release之前调用

     1 import threading,random,time
     2 
     3 gMoney = 1000 #此全局变量用来保存生产和消费的钱
     4 gCondition = threading.Condition()
     5 gTotalTimes = 10  #此全局变量用来保存需要消费多少次
     6 gtimes = 0   #此全局变量用来保存生产了多少次
     7 
     8 class producer(threading.Thread):  #生产者
     9     def run(self):
    10         global gMoney,gtimes
    11         while True:
    12             money = random.randint(100,1000)
    13             gCondition.acquire()     #上锁
    14             if gtimes >= gTotalTimes:  #如果生产了10次,就退出循环
    15                 gCondition.release()
    16                 break
    17             gMoney += money
    18             print('%s生产了%d元钱,剩余%d元钱' %(threading.current_thread(),money,gMoney))
    19             gtimes += 1
    20             gCondition.notify_all()
    21             gCondition.release()    #解锁
    22             time.sleep(0.5)
    23 
    24 class consumer(threading.Thread):  #消费者
    25     def run(self):
    26         global gMoney
    27         while True:
    28             money = random.randint(100,1000)
    29             gCondition.acquire()
    30             while gMoney < money:
    31                 if gtimes >= gTotalTimes:
    32                     gCondition.release()
    33                     return
    34                 else:
    35                     print('%s准备消费%d元钱,剩余%d元钱,余额不足' % (threading.current_thread(), money, gMoney))
    36                 gCondition.wait()
    37             gMoney -= money
    38             print('%s消费了%d元钱,剩余%d元钱' %(threading.current_thread(),money,gMoney))
    39             gCondition.release()  #此处的解锁如果放在if代码块里面,可能会造成死锁
    40             time.sleep(0.5)
    41 
    42 def main():
    43     for i in range(5):
    44         t = consumer(name='消费者线程%d' %i)
    45         t.start()
    46 
    47     for i in range(5):
    48         t = producer(name='生产者线程%d' %i)
    49         t.start()
    50 
    51 if __name__ == '__main__':
    52     main()
  • 相关阅读:
    1242 斐波那契数列的第N项
    1256 乘法逆元
    1264 线段相交
    1265 四点共面
    Uva10881 Piotr's Ants
    hdu 5438 Ponds 长春网赛1002
    CodeForces 540D Bad Luck Island 概率dp
    hdu 1281 二分图残量增广
    hdu 2444判定二分图+最大匹配
    hdu 3416 Marriage Match IV
  • 原文地址:https://www.cnblogs.com/GouQ/p/13059982.html
Copyright © 2011-2022 走看看