多线程

 

创建线程

与创建进程类似

方式一

import threading
import time
 
def music(data):
    print("bengin listen music: {}".format(time.ctime()))
    time.sleep(1)
    print(str(data))
    print("music end: {}".format(time.ctime()))
 
def movie(data):
    print("bengin look movie: {}".format(time.ctime()))
    time.sleep(3)
    print(str(data))
    print("movie end: {}".format(time.ctime()))
 
th1 = threading.Thread(target=music, args=("love.mp3",)) #创建线程
th1.start()  ##启动线程
th2 = threading.Thread(target=movie, args=("Anit.avi",))
th2.start()

方式二

通过继承Thread类,重写它的run方法

import threading
import time
 
class MultipleThreading(threading.Thread):
 
    def __init__(self, func, args=(), kwargs=None):
        threading.Thread.__init__(self)
        self.func = func
        self.args = args
        if kwargs is None:
            kwargs = {}
        self.kwargs = kwargs
 
    def run(self): ##重写run()方法
        print('func_name is: {}'.format(self.func.__name__))
        return self.func(*self.args, **self.kwargs)
 
def music(data):
    print("bengin listen music: {}".format(time.ctime()))
    time.sleep(2)
    print(str(data))
    print("music end: {}".format(time.ctime()))
 
def movie(data):
    print("bengin look movie: {}".format(time.ctime()))
    time.sleep(5)
    print(str(data))
    print("movie end: {}".format(time.ctime()))
 
th1 = MultipleThreading(music, ("love.mp3",))
th2 = MultipleThreading(movie, ("Anit.avi",))
th1.start()
th2.start()

 

join()方法

join()方法会阻塞主线程,当启动线程之后,只有线程全部结束,主线程才会继续执行,类似多进程

import threading
import time
 
def music(data):
    print("bengin listen music: {}".format(time.ctime()))
    time.sleep(2)
    print(str(data))
    print("music end: {}".format(time.ctime()))
 
def movie(data):
    print("bengin look movie: {}".format(time.ctime()))
    time.sleep(5)
    print(str(data))
    print("movie end: {}".format(time.ctime()))
 
thread_list = []
 
th1 = threading.Thread(target=music, args=("love.mp3",))
th2 = threading.Thread(target=movie, args=("Anit.avi",))
thread_list.append(th1)
thread_list.append(th2)
 
for th in thread_list:
    th.start()
    th.join()
 
print("main thread continue: {}".format(time.ctime()))

 

守护线程

守护进程会随着主进程代码执行完后就结束

守护线程则会在主线程结束之后并且等待其他子线程结束后才结束

from threading import Thread
import time

def func1():
    while True:
        print('func1')
        time.sleep(1)

def func2():
    while True:
        print('func2')
        time.sleep(1)

t1 = Thread(target=func1,)
t1.daemon = True  # 或者使用t1.setDaemon(True)
t1.start()
t2 = Thread(target=func2,)
t2.start()
print('主线程')

 

线程锁

互斥 Lock

from threading import Thread, Lock
import time

def func(lock):
    global n
    lock.acquire()
    temp = n
    time.sleep(1)
    n = temp - 1
    lock.release()
n
= 10 t_list = [] lock = Lock() for i in range(10): t = Thread(target=func, args=(lock,)) t.start() t_list.append(t) for t in t_list: t.join() print(n)

递归锁 RLock

一个线程可以多次acquire(),只要acquire()一次,其他线程就不能acquire(),所以,acquire() n 次后需要release() n 次其他线程才能acquire()。

通过RLock实现哲学家就餐:

from threading import RLock, Thread
import time
knife_lock = fork_lock = RLock()    # 设置为同一个锁


def eat1(name):
    knife_lock.acquire()
    print('%s拿到刀子' % name)
    fork_lock.acquire()
    print('%s拿到叉子' % name)
    print('%s eating' % name)
    fork_lock.release()
    knife_lock.release()


def eat2(name):
    fork_lock.acquire()
    print('%s拿到插子' % name)
    time.sleep(1)
    knife_lock.acquire()
    print('%s拿到刀子' % name)
    print('%s eating' % name)
    knife_lock.release()
    fork_lock.release()


Thread(target=eat1, args=('1',)).start()
Thread(target=eat2, args=('2',)).start()
Thread(target=eat1, args=('3',)).start()
Thread(target=eat2, args=('4',)).start()

 

信号量

在Python中存在两种信号量,一种就是纯粹的Semphore,还有一种就是BoundedSemaphore。

Semphore和BoundedSemaphore主要区别:

  Semphore:  在调用release()函数时,不会检查,增加的计数是否超过上限(没有上限,会一直上升)
  BoundedSemaphore:在调用release()函数时,会检查,增加的计数是否超过上限,这样就保证了使用的计数

Semphore示例

import threading
import random
 
semaphore = threading.Semaphore(0) ##信号量个数,默认值为1。
 
def producer():
    global item
    item = random.randrange(1, 1000)
    semaphore.release() ## 信号量的 release() 可以提高计数器然后通知其他的线程, 信号量个数增加+1
    print("producer notify : produced item number %s" % item)
 
def consumer():
    print("consumer is waiting....")
    global item
    semaphore.acquire(timeout=3) ##信号量个数减1
    # 如果信号量的计数器到了0,就会阻塞 acquire() 方法,
    # 直到得到另一个线程的通知。如果信号量的计数器大于0,就会对这个值-1然后分配资源。
    print("consume notify : consume item number %s" % item)
 
 
if __name__ == "__main__":
    for i in range(3):
        th1 = threading.Thread(target=producer)
        th2 = threading.Thread(target=consumer)
        th1.start()
        th2.start()
        th1.join()
        th2.join()
    print("teminated")

 

BoundedSemaphore示例

import threading
 
semaphore = threading.BoundedSemaphore(1)
def func(num):
    semaphore.acquire()
    print("the number is: {}".format(num))
    semaphore.release()
    # 再次释放信号量,信号量加一,这是超过限定的信号量数目,这时会报错ValueError: Semaphore released too many times
    semaphore.release()
 
if __name__ == "__main__":
    num = 12
    th1 = threading.Thread(target=func, args=(num,))
    th1.start()
    th1.join()

 

使用条件condition控制线程数据同步

condition指的是应用程序状态的改变。这是另一种同步机制,其中某些线程在等待某一条件发生,其他的线程会在该条件发生的时候进行通知。一旦条件发生,线程会拿到共享资源的唯一权限。

Condition(条件变量)通常与一个锁关联。需要在多个Contidion中共享一个锁时,可以传递一个Lock/RLock实例给构造方法,否则它将自己生成一个RLock实例。

from threading import Thread, Condition
import random
 
items = []
cond = Condition()
 
class Producer(Thread):
    def __init__(self):
        Thread.__init__(self)
 
    def produce(self):
        global cond
        global items
        cond.acquire()
        if len(items) >= 2:
            print("items is 2, wait consume")
            cond.notify() ##当items数量不小于2时,通知consumer消费,线程阻塞
            cond.wait()
        data = random.randrange(1, 1000)
        items.append(data)
        print("produce data is: {}".format(data))
        print("increase items lenth to: {}".format(len(items)))
        cond.release()
 
    def run(self):
        for i in range(5):
            self.produce()
 
class Consumer(Thread):
    def __init__(self):
        Thread.__init__(self)
 
    def consume(self):
        global cond
        global items
        cond.acquire()
        if len(items) <= 0:  ##当items数量小于等于0时,通知producer产生数据,线程阻塞
            print("items is 0, wait producer")
            cond.notify()
            cond.wait()
        data = items.pop()
        print("consume data is: {}".format(data))
        print("decrease items lenth to: {}".format(len(items)))
        cond.release()
 
    def run(self):
        for i in range(5):
            self.consume()
 
if __name__ == "__main__":
    producer = Producer()
    consumer = Consumer()
    producer.start()
    consumer.start()
    producer.join()
    consumer.join()

 

使用事件Event控制线程同步

Event是线程通信最为简单的机制,一个线程抛出一个信号,另外线程等待这个信号。

事件是线程之间用于通讯的对象。有的线程等待信号,有的线程发出信号。基本上事件对象都会维护一个内部变量,可以通过 set() 方法设置为 true ,也可以通过 clear() 方法设置为 false。 wait() 方法将会阻塞线程,直到内部变量为 true 。

Event(事件)处理的机制:全局定义了一个内置标志Flag,如果Flag值为 False,那么当程序执行 event.wait方法时就会阻塞,如果Flag值为True,那么event.wait 方法时便不再阻塞。

Event其实就是一个简化版的 Condition。Event没有锁,无法使线程进入同步阻塞状态。当多个线程监听某一个触发条件时,如果全部线程都依赖这个触发条件,可以使用Event;如果需要精确控制某些线程被触发,则需要使用Condition。

Event class threading.Event :管理一个内部flag(True or False)
is_set() :判断内部flag是否True
set() :将内部flag设置为True,并通知所有处于等待阻塞状态的线程恢复运行状态。
clear() :将内部flag设置为False
wait(timeout=None) : 阻塞直到内部flag为True,或者timeout时间到。如果标志为True将立即返回,否则阻塞线程至等待阻塞状态,等待其他线程调用set()。

from threading import Thread, Event
import time
 
class TrafficLight(Thread):
    def __init__(self, event, green_cnt, red_cnt):
        super(TrafficLight, self).__init__()
        self.event = event
        self.green_cnt = green_cnt
        self.red_cnt = red_cnt
        self.interval = self.green_cnt + self.red_cnt
 
    def run(self):
        count = 0
        self.event.set()   ##绿灯亮
        while True:
            if count <= self.green_cnt:
                self.event.set() ##绿灯亮
                print("绿灯亮了 {}s,请通行".format(count))
            elif self.green_cnt < count <= self.interval:
                self.event.clear() ##红灯亮
                print("红灯亮 {}s,车辆禁止通行".format(count))
            elif count > self.interval:
                self.event.set() ##绿灯亮
                count = 0
                print("绿灯亮了 {}s,请通行".format(count))
 
            time.sleep(1)
            count = count + 1
 
class Car(Thread):
    def __init__(self, event, car_name):
        super(Car, self).__init__()
        self.event = event
        self.car_name = car_name
 
    def run(self):
        while True:
            if self.event.is_set(): # 有标志位,代表是绿灯
                print("{} is runnning".format(self.car_name))
                time.sleep(1)
            else: # 如果不是绿灯就代表红灯
                self.event.wait() ##阻塞,直到event被set才继续执行
                print("{} is waiting".format(self.car_name))
 
if __name__ == "__main__":
    event = Event()
    thread_list = []
    car_list = ["BMW", "Ford", "BenZ"]
    thread_light = TrafficLight(event, 1, 2)
    thread_list.append(thread_light)
    for car_name in car_list:
        car_th = Car(event, car_name) ##所有的车收到event事件同时开始执行/阻塞
        thread_list.append(car_th)
 
    for th in thread_list:
        th.start()
 
    for th in thread_list:
        th.join()

 

使用栅栏Barrier控制数据同步

import threading
from threading import Barrier, Thread
 
def prepare_user_data(user):
    print("user {} is ready".format(user))
 
def prepare_car_data(car):
    print("car {} is ready".format(car))
 
def ready():
    print("{} 数据准备好了".format(threading.current_thread().name))
 
class RunThreading(Thread):
    def __init__(self, barries, target=None, args=(), kwargs={}):
        super(RunThreading, self).__init__(target=None, args=args, kwargs=kwargs)
        self._barries = barries
        self.func = target
        self.args = args
        self._kwargs = kwargs
 
    def run(self):
        print("data prepare......")
        data = self.func(*self.args, **self._kwargs)
        self._barries.wait()
        return data
 
if __name__ == "__main__":
    data = {"Leo": "沪111111", "Bruce": "沪222222", "Frank": "沪333333"}
    cnt = len(data)
    print("并发测试数据准备...")
    barrier = Barrier(cnt, action=ready, timeout=10)
    thread_list = []
    for k, v in data.items():
        thread_user = RunThreading(barrier, target=prepare_user_data, args=(k,))
        thread_car = RunThreading(barrier, target=prepare_car_data, args=(v,))
        thread_list.append(thread_user)
        thread_list.append(thread_car)
 
    for thread in thread_list:
        thread.start()
 
    for thread in thread_list:
        thread.join()
    if barrier.broken:
        print("数据准备出异常了,请重新准备数据")

 

使用Queue进行线程间数据通讯

线程之间如果要共享资源或数据的时候,可能变的非常复杂。Python的threading模块提供了很多同步原语,包括信号量,条件变量,事件和锁。如果可以使用这些原语的话,应该优先考虑使用这些,而不是使用queue(队列)模块。

 Queue提供的一个线程安全的多生产者,多消费者队列,自带锁, 多线程并发数据交换必备。

生产者消费者问题:

from threading import Thread
from queue import Queue
import random, time
class Producer(Thread):
    def __init__(self, queue):
        super(Producer, self).__init__()
        self.queue = queue
 
    def run(self):
        for i in range(10):
            item = random.randint(0, 256)
            self.queue.put(item) ##插入队列
            print('Producer notify: item N° %d appended to queue by %s' % (item, self.name))
            time.sleep(1)
 
class Consumer(Thread):
    def __init__(self, queue):
        Thread.__init__(self)
        self.queue = queue
 
    def run(self):
        while True:
            item = self.queue.get() ##从队列中取出数据
            print('Consumer notify : %d popped from queue by %s' % (item, self.name))
            self.queue.task_done()
            
if __name__ == '__main__':
    queue = Queue()
    thread_list = []
    t1 = Producer(queue)
    t2 = Consumer(queue)
    t3 = Consumer(queue)
    thread_list.append(t1)
    thread_list.append(t2)
    thread_list.append(t3)
    for th in thread_list:
        th.start()
    for th in thread_list:
        th.join()

 

定时器Timer

与Thread类似,只是要等待一段时间后才会开始运行,单位秒。

import threading, time
 
def data_ready():
    print("data is ready at {}".format(time.ctime()))
 
if __name__ == "__main__":
    th = threading.Timer(5, data_ready)
    th.start()
    while threading.active_count() > 1:
        print("数据还没ready:{}".format(time.ctime()))
        time.sleep(1)

 

线程池

import time
from concurrent.futures import ThreadPoolExecutor
# from concurrent.futures import ProcessPoolExecutor    # 进程池用法与线程池完全相同

def func(n):
    time.sleep(0.5)
    print(n)
    return n*n

t_pool = ThreadPoolExecutor(max_workers=5)  # 一般不超过CPU*5
t_list = []
for i in range(20):
    t = t_pool.submit(func, i)
    t_list.append(t)
t_pool.shutdown()   # close()+join()
print('主线程')
for t in t_list:
    print(t.result())

 

posted @ 2019-10-19 20:07  tianqibucuo  阅读(134)  评论(0)    收藏  举报