Python实现多线程

Python实现多线程

  • 一、什么是线程
  • 二、Python的GIL
  • 三、Python的线程与threading模块
    • 3.1 两种调用方式
      • 3.1.1 直接调用
      • 3.1.2 继承式调用
    • 3.2 threading.Thread 的实例方法
      • 3.2.1 join()&Daemon()
      • 3.2.2 其他方法
  • 四、同步锁
  • 五、递归锁
  • 六、同步条件event
  • 七、信号量
  • 八、线程队列
  • 九、生产者消费者模型 --通过线程队列实现

一、什么是线程

线程 可以理解为在进程中独立运行的子程序,是操作系统能够运行调度的最小单位。

举个不是特别恰当的例子,比如你正在听歌,然后你突然发现一首歌特别好听,你就想下载下来,这个时候你想边听边下载,但是这个时候就有一个尴尬的事情,如果你只有一个进程,会造成你在下载的时候不能听歌;如果有多个进程,进程A负责听,进程B负责下载。这时候又会出现两个进程协作的问题,进程执行过程中拥有独立的内存单元,彼此间资源不共享,不停地切换进程会造成性能上的损伤。这时候,如果能有一个机制,可以使两个任务共享资源,这样就能解决进程通信问题,降低来回切换的损耗,那就非常好了。这,就是线程。

线程特点:
1. 一个进程至少有一个线程;
2. 多个线程共享内存;
3. 线程不能够独立执行,必须依存于进程

二、Python的GIL

在CPython中,全局解释器锁(GIL)是一个互斥锁,它可以防止多个本机线程同时执行Python字节码。这个锁是必需的,主要是因为CPython的内存管理不是线程安全的。(然而,由于GIL的存在,其他功能已经依赖于它所执行的保证。)

这就意味着:无论你启多少个线程,你有多少个cpu, Python在执行的时候在同一时刻只允许一个线程运行。

三、Python的线程与threading模块

3.1 两种调用方式

3.1.1 直接调用

import threading  #线程
import time


def Hi(num):
    print("hello %d" %num)
    time.sleep(3)
    print("world")


if __name__ == '__main__':

    t1=threading.Thread(target=Hi,args=(10,))  #创建一个线程对象
    t2=threading.Thread(target=Hi,args=(9,))  #创建一个线程对象
    
    t1.start() #启动线程
    t2.start() #启动另一个线程
  
    print("ending.........")
执行结果:
hello 10
hello 9
ending.........
(将在此处停留三秒)
world
world

3.1.2 继承式调用

import threading
import time


class MyThread(threading.Thread):

    def __init__(self,num):
        threading.Thread.__init__(self) #最好采用super()
        self.num = num

    def run(self):#定义每个线程要运行的函数

        print("running on number:%s" %self.num)

        time.sleep(3)

if __name__ == '__main__':

    t1 = MyThread(1)
    t2 = MyThread(2)
    t1.start()
    t2.start()
    
    print("ending......")

自定义一个类,继承 threading.Thread,然后重新 run 方法

执行结果:
running on number:1
running on number:2
ending......
(停留三秒,然后结束运行)

3.2 threading.Thread 的实例方法

3.2.1 join()&Daemon()

import threading
import time


def music():
    print("begin to listen %s"%time.ctime())
    time.sleep(3)
    print("stop to listen %s" % time.ctime())


def game():
    print("begin to play game %s" % time.ctime())
    time.sleep(5)
    print("stop to play game %s" % time.ctime())


if __name__ == '__main__':

    t1 = threading.Thread(target=music)

    t2 = threading.Thread(target=game)

    t2.setDaemon(True)
    # t1.setDaemon(True)

    t1.start()
    t2.start()

    t1.join()
    # print(threading.currentThread())
    # print(threading.enumerate())
    # print(threading.activeCount())

    print("ending....")

猜想运行结果:
begin to listen Wed Jul 22 23:26:21 2020
begin to play game Wed Jul 22 23:26:21 2020
ending....
stop to listen Wed Jul 22 23:26:24 2020
stop to play game Wed Jul 22 23:26:26 2020


实际运行结果:
begin to listen Wed Jul 22 23:26:21 2020
begin to play game Wed Jul 22 23:26:21 2020
stop to listen Wed Jul 22 23:26:24 2020
ending....

为什么与设想不符?
这便是join()&Daemon方法

join():在子线程完成运行之前,这个子线程的父线程将一直被阻塞。

setDaemon(True):
将线程声明为守护线程,必须在start() 方法调用之前设置, 如果不设置为守护线程程序会被无限挂起。这个方法基本和join是相反的。被设置为守护线程的线程,当主线程结束时,守护线程如果还没有结束,将会随主线程直接结束

3.2.2 其他方法

# run():  线程被cpu调度后自动执行线程对象的run方法
# start():启动线程活动。
# isAlive(): 返回线程是否活动的。
# getName(): 返回线程名。
# setName(): 设置线程名。

threading模块提供的一些方法:
# threading.currentThread(): 返回当前的线程变量。
# threading.enumerate(): 返回一个包含正在运行的线程的list。正在运行指线程启动后、结束前,不包括启动前和终止后的线程。
# threading.activeCount(): 返回正在运行的线程数量,与len(threading.enumerate())有相同的结果。

四、同步锁

当多个线程线程都在同时操作一个共享资源,可能会造成资源破坏可使用同步锁解决问题

import threading, time


def sub():
    global num					#在每个线程都获得这个共享量
    lock.acquire()
    temp=num
    time.sleep(0.0004)
    num=temp-1
    lock.release()


if __name__ == "__main__":
    num = 100					#设置一个共享量
    l = []
    lock = threading.Lock()		#实例化出一个同步锁
    for i in range(100):
        t = threading.Thread(target=sub)
        t.start()
        l.append(t)

    for i in l:
        i.join()

    print(num)

五、递归锁

在线程间共享多个资源的时候,如果两个线程分别占有一部分资源并且同时等待对方的资源,就会造成死锁,因为系统判断这部分资源都正在使用,所有这两个线程在无外力作用下将一直等待下去。
此时可使用递归锁

import  threading
import time


class MyThread(threading.Thread):

    def actionA(self):

        r_lcok.acquire() #count=1
        print(self.name,"gotA",time.ctime())
        time.sleep(2)
        r_lcok.acquire() #count=2

        print(self.name, "gotB", time.ctime())
        time.sleep(1)

        r_lcok.release() #count=1
        r_lcok.release() #count=0


    def actionB(self):

        r_lcok.acquire()
        print(self.name, "gotB", time.ctime())
        time.sleep(2)

        r_lcok.acquire()
        print(self.name, "gotA", time.ctime())
        time.sleep(1)

        r_lcok.release()
        r_lcok.release()


    def run(self):

        self.actionA()
        self.actionB()


if __name__ == '__main__':

    # A=threading.Lock()
    # B=threading.Lock()

    r_lcok=threading.RLock()		#获得一个递归锁对象
    L=[]

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


    for i in L:
        i.join()

    print("ending....")

   递归锁相当于内部有一个计数器
   acquire一次,计数器+1
   release一次,计数器-1
   只要count>1,内存不释放

六、同步条件event

event是简单的同步对象;event表示内部标志,
线程可以等待标志被设置,或者自己设置或清除标志。

import threading, time


class Boss(threading.Thread):

    def run(self):
        print("BOSS:今晚大家都要加班到22:00。")
        print(event.isSet())# False
        time.sleep(4)
        event.set()
        time.sleep(5)
        print("BOSS:<22:00>可以下班了。")
        print(event.isSet())
        event.set()


class Worker(threading.Thread):
    def run(self):

        event.wait()        # 一旦event被设定,等同于pass

        print("Worker:哎……命苦啊!")
        time.sleep(1)
        event.clear()
        event.wait()
        print("Worker:OhYeah!")


if __name__=="__main__":
    
    event = threading.Event()

    threads = []
    for i in range(5):
        threads.append(Worker())
    threads.append(Boss())
    for t in threads:
        t.start()
    for t in threads:
        t.join()

    print("ending.....")

七、信号量

信号量用来控制线程并发数的

import threading,time


class myThread(threading.Thread):
    def run(self):

        if semaphore.acquire():
            print(self.name)
            time.sleep(3)
            semaphore.release()


if __name__=="__main__":
    semaphore = threading.Semaphore(25)

    thrs = []
    for i in range(100):
        thrs.append(myThread())
    for t in thrs:
        t.start()

八、线程队列

import threading,time

li=[1,2,3,4]


def pri():
    while li:
        a=li[-1]
        print(a)
        time.sleep(1)
        li.remove(a)
        # try:
        #     li.remove(a)
        # except Exception as e:
        #     print('----',a,e)

t1=threading.Thread(target=pri,args=())
t1.start()
t2=threading.Thread(target=pri,args=())
t2.start()

import queue

q = queue.Queue(3)
q.put(1)
q.put("hello")
q.put("world")
print(q.full())
# q.put(233,False)

while 1:
    # data = q.get()
    # print(data)
    # print("----------")

    # data = q.get()
    data = q.get(False)
    print(data)
    print("----------")
    if q.empty():
        break

print("ending-----")


创建一个“队列”对象
import Queue
q = Queue.Queue(maxsize = 10)
Queue.Queue类即是一个队列的同步实现。队列长度可为无限或者有限。可通过Queue的构造函数的可选参数maxsize来设定队列长度。如果maxsize小于1就表示队列长度无限。

将一个值放入队列中
q.put(10)
调用队列对象的put()方法在队尾插入一个项目。put()有两个参数,第一个item为必需的,为插入项目的值;第二个block为可选参数,默认为1。如果队列当前为空且block为1,put()方法就使调用线程暂停,直到空出一个数据单元。如果block为0,put方法将引发Full异常。

将一个值从队列中取出
q.get()
调用队列对象的get()方法从队头删除并返回一个项目。可选参数为block,默认为True。如果队列为空且block为True,
get()就使调用线程暂停,直至有项目可用。如果队列为空且block为False,队列将引发Empty异常。

Python Queue模块有三种队列及构造函数:
1、Python Queue模块的FIFO队列先进先出。   class queue.Queue(maxsize)
2、LIFO类似于堆,即先进后出。               class queue.LifoQueue(maxsize)
3、还有一种是优先级队列级别越低越先出来。        class queue.PriorityQueue(maxsize)

此包中的常用方法(q = Queue.Queue()):
q.qsize() 返回队列的大小
q.empty() 如果队列为空,返回True,反之False
q.full() 如果队列满了,返回True,反之False
q.full 与 maxsize 大小对应
q.get([block[, timeout]]) 获取队列,timeout等待时间
q.get_nowait() 相当q.get(False)
非阻塞 q.put(item) 写入队列,timeout等待时间
q.put_nowait(item) 相当q.put(item, False)
q.task_done() 在完成一项工作之后,q.task_done() 函数向任务已经完成的队列发送一个信号
q.join() 实际上意味着等到队列为空,再执行别的操作

九、生产者消费者模型 --通过线程队列实现

import time,random
import queue,threading

q = queue.Queue()

def Producer(name):
  count = 0
  while count <10:
    print("making........")
    time.sleep(5)
    q.put(count)
    print('Producer %s has produced %s baozi..' %(name, count))
    count +=1
    # q.task_done()

    q.join()
    print("ok......")

def Consumer(name):
  count = 0
  while count <10:
        time.sleep(random.randrange(4))
    # if not q.empty():
        print("waiting.....")
        # q.join()
        data = q.get()
        print("eating....")
        time.sleep(4)

        q.task_done()
        #print(data)
        print('\033[32;1mConsumer %s has eat %s baozi...\033[0m' %(name, data))
    # else:
    #     print("-----no baozi anymore----")
        count +=1

p1 = threading.Thread(target=Producer, args=('A君',))
c1 = threading.Thread(target=Consumer, args=('B君',))
c2 = threading.Thread(target=Consumer, args=('C君',))
c3 = threading.Thread(target=Consumer, args=('D君',))

p1.start()
c1.start()
c2.start()
c3.start()

你可能感兴趣的:(Python之网络编程)