[7]python 多线程

GIL 全局解析器锁

python中的线程只有获取到GIL后才能执行。
在某一时刻,只有一个线程在一个cpu上运行。无法将多个线程映射到多个cpu上运行。
线程在时间片用完、执行多条字节代、遇到IO阻塞,会释放GIL

多线程实现

  • 通过定义函数
import threading

def A():
    print("thread A")

def B():
    print("thread B")


if __name__ == "__main__":
    t1 = threading.Thread(target=A)
    t2 = threading.Thread(target=B)
    t1.start()
    t2.start()
  • 通过继承 threading.Thread类
import threading

class A(threading.Thread):
    def run(self):
        print("thread A")

class B(threading.Thread):
    def run(self):
        print("thread B")


if __name__ == "__main__":
    t1 = A()
    t2 = B()
    t1.start()
    t2.start()

线程间通信

  • 通过共享变量进行通信
import threading

#定义一个全局
var = 0

def A():
    global var
    var += 1000

def B():
    global var
    print("var=",var)


if __name__ == "__main__":
    t1 = threading.Thread(target=A)
    t2 = threading.Thread(target=B)
    t1.start()
    t2.start()
  • 通过消息队列进行通信
import threading
import queue

def A(q):
    """
    producter
    :param queue.Queue q:
    """
    for i in range(100):
        q.put('are you ok '+str(i))

def B(q):
    """
    comsumer
    :param queue.Queue q:
    :return:
    """
    while True:
        print(q.get())


if __name__ == "__main__":
    q = queue.Queue(1000)
    t1 = threading.Thread(target=A, args=(q,))
    t2 = threading.Thread(target=B, args=(q,))
    t1.start()
    t2.start()

    t1.join()
    t2.join()

Lock, Rlock 锁

解决对同一资源的并发访问

import threading
n = 0
def A():
    global n
    for i in range(1000000):
        n += 1

def B():
    global n
    for i in range(1000000):
        n -= 1

if __name__ == "__main__":
    t1 = threading.Thread(target=A)
    t2 = threading.Thread(target=B)

    t1.start()
    t2.start()

    t1.join()
    t2.join()
    # n每次运行的结果不一致,正常结果应该等于0
    # 原因时A,B两个线程交替运行,他们读取到的共享变量n不一定时最新的,在他们更新变量n之前,n已经被修改过
    print(n)

通过使用锁,来解决并发访问共享资源

import threading
n = 0
l = threading.Lock()
def A():
    global n
    for i in range(1000000):
        l.acquire()
        n += 1
        # 将对共享资源的操作写在 这两个函数调用之间
        l.release()

def B():
    global n
    for i in range(1000000):
        l.acquire()
        n -= 1
        l.release()

if __name__ == "__main__":
    t1 = threading.Thread(target=A)
    t2 = threading.Thread(target=B)

    t1.start()
    t2.start()

    t1.join()
    t2.join()
    print(n)

使用Rlock,能够避免在同一个线程内重复申请锁,造成死锁

import threading
n = 0
l = threading.RLock()
def A():
    global n
    for i in range(1000000):
        l.acquire()
        l.acquire()
        # 在同一个线程内,重复申请锁,也不会造成死锁
        n += 1
        # 需要注意,申请了多少次锁,就要释放多次,不然线程会一直占用该锁
        l.release()
        l.release()

def B():
    global n
    for i in range(1000000):
        l.acquire()
        n -= 1
        l.release()

if __name__ == "__main__":
    t1 = threading.Thread(target=A)
    t2 = threading.Thread(target=B)

    t1.start()
    t2.start()

    t1.join()
    t2.join()
    print(n)

使用condition ,令两个线程配合工作

# 按顺序打印 1、2、3、4、5、6...
import threading

class A(threading.Thread):
    def __init__(self,cond):
        """
        :param threading.Condition cond:
        """
        self.cond = cond
        super().__init__(name="A")
    def run(self):
        with self.cond:
            print(1)
            self.cond.notify()
            self.cond.wait()
            print(3)
            self.cond.notify()

class B(threading.Thread):
    def __init__(self, cond):
        """
        :param threading.Condition cond:
        """
        self.cond = cond
        super().__init__(name="B")
    def run(self):
        with self.cond:
            self.cond.wait()
            print(2)
            self.cond.notify()
            self.cond.wait()
            print(4)

if __name__ == '__main__':
    cond = threading.Condition()
    t1 = A(cond)
    t2 = B(cond)
    t2.start()
    t1.start()

信号量,合理的分配多个共享资源给线程

import threading
import time

def A(i, sem):
    """
    :param threading.Semaphore sem:
    """
    # 先申请使用信号量
    sem.acquire()
    time.sleep(i//2)
    print("this is {}".format(i))
    # 业务处理完毕后释放
    sem.release()

if __name__ == "__main__":
    sem = threading.Semaphore(3)

    for n in range(10):
        t = threading.Thread(target=A, args=(n,sem))
        t.run()

多线程池

import concurrent.futures

# 使用线程池,能够限制创建的线程数量
# 能够查看线程的执行状态,获取线程返回的结果
def A(no):
    str = "this is {}".format(no)
    print(str)
    return "result :"+str


if __name__ == '__main__':
    ex = concurrent.futures.ThreadPoolExecutor(max_workers=2) #max_workers,限制能够同时运行的线程数量
    t1 = ex.submit(A,1) # 提交线程任务,方法会立即放回
    t2 = ex.submit(A,2)

    print(t1.cancel()) #取消线程,只有在线程未开始执行时,才能取消
    print(t1.done()) #查看线程是否执行完毕
    print(t1.result()) #查看线程放回的结果

通过 as_completed 获取已完成任务的线程

import concurrent.futures
import time

def A(no):
    str = "this is {no}\n".format(no=no)
    #print(str)
    time.sleep(no)
    return str

if __name__ == '__main__':
    ex = concurrent.futures.ThreadPoolExecutor(3)
    pool = {ex.submit(A,i) for i in range(4)}

    #1、 as_completed 获取所有已完成线程的返回结果,如果线程未完成,会一直阻塞。as_completed调用后会返回一个生成器
    for item in concurrent.futures.as_completed(pool):
        print(item.result())

通过 ThreadPoolExecutor 的map方法 ,批量提交线程,并且获取结果

import concurrent.futures
import time

def A(no):
    str = "this is {no}\n".format(no=no)
    time.sleep(no)
    return str

if __name__ == '__main__':
    ex = concurrent.futures.ThreadPoolExecutor(3)

    for result in  ex.map(A,range(3)): # 能够通过遍历 map的返回值,获取线程执行结果,遇到没有执行完的线程,会阻塞
        print(result)

多进程编成

python 中的多线程,在同一个时间点中只有一个线程占用cpu运行,无法多个线程映射到多个cpu上。
多进程能解决pyhon多线程的缺点,适合于计算密集型的场景。但是多进程的创建和切换系统开销比多线程大,在io密集型的场景中,多线程更加合适。

# 比较多线程 和 多进程在计算密集型场景下的执行时间
import concurrent.futures
import time


def fib(n):
    if n <= 2:
        return 1
    else:
        return fib(n-1) + fib(n-2)

def thread():
    pre_time = time.time()
    with concurrent.futures.ThreadPoolExecutor(max_workers=20) as ex:
        pool = [ex.submit(fib, i) for i in range(30,35)]
        for item in concurrent.futures.as_completed(pool):
            pass
    after_time = time.time()
    print("thread execute {}".format(after_time-pre_time))


def process():
    pre_time = time.time()
    with concurrent.futures.ProcessPoolExecutor(max_workers=20) as ex:
        for item in ex.map(fib,  range(30,35)):
            pass
    after_time = time.time()
    print("process execute {}".format(after_time-pre_time))


if __name__ == '__main__':
    thread() #输出 thread execute 2.5126142501831055
    process() #输出 process execute 1.0493500232696533

进程编程

# 创建进程
import multiprocessing

def A(i):
    print("this is process {}".format(i))

if __name__ == "__main__":
    p = multiprocessing.Process(target=A, args=(1,))
    p.start() #启动进程
    p.join()  # 等待子进程完成

# 创建进程池
import multiprocessing

def A(i):
    return "this is process {}".format(i)

if __name__ == "__main__":
    # 创建线程池
    pool = multiprocessing.Pool(5)
    # 提交进程任务任务
    result = pool.apply_async(A,(1,))
    # 关闭进程池,不能继续提交任务
    pool.close()
    # 等待进程都处理完毕
    pool.join()
    # 获取进程的返回值
    print(result.get())
    
    #imap 

进程间通信

  • 管道
import multiprocessing
import time
import os

def A(p):
    """
    :param multiprocessing.Connection p:
    """
    p.send("are you ok\n")
    pass

def B(p):
    """
    :param multiprocessing.Connection p:
    """
    print("B recv: ",p.recv())

if __name__ == "__main__":
    # 使用 multiprocessing.Pipe 创建管道
    pr, pw = multiprocessing.Pipe()
    p1 = multiprocessing.Process(target=A, args=(pw,))
    p2 = multiprocessing.Process(target=B, args=(pr,))
    p1.start()
    p2.start()
    p1.join()
    p2.join()

  • 消息队列
import multiprocessing
import time
import os

def A(q):
    for i in range(20):
        q.put(i)
        print(i)
        time.sleep(0.5)

def B(q):
    while True:
        i = q.get()
        print("pid = {},get i = {}".format(os.getpid(), i))

if __name__ == "__main__":
    # 使用消息队列通信,必须使用Manager创建队列
    q = multiprocessing.Manager().Queue(10)
    pool = multiprocessing.Pool(4)

    result = []
    result.append(pool.apply_async(A,(q,)))
    result.extend([pool.apply_async(B,(q,)) for i in range(3)])

    pool.close()
    pool.join() 	
  • 共享内存
    可以通过Manager创建,Array、dict、list等共享内存
import multiprocessing
import time
import os

def A(d):
    """
    :param dict d:
    """
    d.update({'name':'tom'})

def B(d):
    """
    :param dict d:
    """
    print(d.get('name','cat'))


if __name__ == "__main__":
    # 使用 multiprocessing.Pipe 创建管道
    d = multiprocessing.Manager().dict()
    p1 = multiprocessing.Process(target=A, args=(d,))
    p2 = multiprocessing.Process(target=B, args=(d,))
    p1.start()
    p2.start()
    p1.join()
    p2.join()
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值