zoukankan      html  css  js  c++  java
  • 线程与进程

    单核心的CPU和多核心的CPU实现多任务的基本方法。

    即使多核心的CPU真正实现了并行多任务的工作,但是任务的数量远多于核心数,因此,多任务一般是将多个任务轮流分配到每个核心上执行。

    实现多任务的方法可以从几个方面着手:

    多进程、多线程、协程、多进程+多线程

    并行和并发的概念(提纲)

    线程(threading)

    单线程处理

    import time
    
    def eat():
        print('吃饭')
        time.sleep(5)
        print('吃饭结束')
    
    def whtch():
        print('看手机')
        time.sleep(5)
        print('看手机结束')
    
    # 使用单线程
    def main():
        eat()
        whtch()
    
    if __name__ == '__main__':
        print(time.time())
        main()
        print(time.time())
    

    多线程处理

    import threading
    import time
    
    
    def eat():
        print('吃饭')
        time.sleep(5)
        print('吃饭结束')
    
    
    def whtch():
        print('看手机')
        time.sleep(5)
        print('看手机结束')
    
    
    def main():
        # 创建一个子线程对象
        e = threading.Thread(target=eat)
        # 启动子线程
        e.start()
        # 又创建一个子线程对象
        w = threading.Thread(target=whtch)
        w.start()
    
    
    if __name__ == '__main__':
        print(time.time())
        main()
        print(time.time())
    

    查看程序中的所有线程数量

    import threading
    import time
    
    def eat():
        print('吃饭')
        time.sleep(2)
        print('吃饭结束')
    
    def whtch():
        print('看手机')
        time.sleep(5)
        print('看手机结束')
    
    def main():
        e = threading.Thread(target=eat)
        e.start()
        w = threading.Thread(target=whtch)
        w.start()
    
    if __name__ == '__main__':
    
        main()
    
        while True:
            # 查看所有的线程
            # 我们可以看到,主线程是最后结束的
            print(threading.enumerate())
            time.sleep(1)
            #if len(threading.enumerate())  <= 1:
            #    break
    

    使用自定义类进行多线程处理

    通过前面的代码,我们可以看到,如果想打开一个子线程,只需要给线程执行一个处理函数,然后启动这个线程即可。但是我们能否使用类进行多线程的编写呢?当然是可以的。

    import threading
    import time
    
    # 我们自定义的类必须要继承threading.Thread
    class XXX(threading.Thread):
    
        # 这里必须要定义一个run方法
        def run(self):
            print(threading.enumerate())
            # self.name中保存的是当前线程的名字
            print(self.name)
            time.sleep(2)
    
    if __name__ == '__main__':
    
        x1 = XXX()
        x1.start()
    
        x2 = XXX()
        x2.start()
    

    关于Python中全局变量的使用

    在python的函数中,针对全局变量的使用:

    如果使用的时候,需要使全局变量的指向发生变化,那么在使用的时候必须在全局变量前加上global

    如果使用的时候,仅仅是更改了全局变量指向的值,指向并没有发生变化的话,使用的时候不需要在全局变量前加global

    a = 1
    
    def g1():
        # global a
        a = 10
    
    g1()
    # 在函数内部,如果不使用global,那么就不会使用全局变量a
    print(a)
    
    b = [20, 30]
    
    def g2():
        b[0] = 111
    
    g2()
    print(b)
    

    在各个线程中,全局变量可以共享

    import threading
    import time
    
    num = 0
    
    def fun1():
        global num
        for i in range(0, 500):
            num += 1
    
    def fun2():
        global num
        for i in range(0, 500):
            num += 1
    
    if __name__ == '__main__':
    
        t1 = threading.Thread(target=fun1)
        t2 = threading.Thread(target=fun2)
        t1.start()
        t2.start()
        
        time.sleep(1)
        print(num)
    
    

    创建进程对象的时候,为要执行的函数传递参数

    我们知道,对于单个cpu来说,其某一个时间点,只能执行一个任务,我们之所以看着好像可以执行多任务,其实是因为操作系统将我们的各个任务分成了很多小任务,这些小任务交替执行,我们看着好像是同时执行而已。

    那么,我们可以想到,我们每个线程执行的是一个函数,这些函数里面有很多语句,如果语句并不是完整的分到一个小任务中,那么共享的东西可能会被覆盖赋值,因此会造成数据不准确。

    import threading
    import time
    
    
    def eat(sec):
        print('吃饭')
        time.sleep(sec)
        print('吃饭结束')
    
    
    def whtch(sec, other):
        print('看手机')
        time.sleep(sec)
        print(other)
        print('看手机结束')
    
    if __name__ == '__main__':
    
        t1 = threading.Thread(target=eat, args=(1,))
        t2 = threading.Thread(target=whtch, args=(5, '和同学聊天'))
        t1.start()
        t2.start()
    
    

    全局变量共享带了的问题

    import threading
    import time
    
    num = 0
    
    def fun1():
        global num
        for i in range(0, 500000):
            num += 1
        print('fun1:%s' % num)
    
    def fun2():
        global num
        for i in range(0, 500000):
            num += 1
        print('fun2:%s' % num)
    
    if __name__ == '__main__':
    
        t1 = threading.Thread(target=fun1)
        t2 = threading.Thread(target=fun2)
        t1.start()
        t2.start()
    
        time.sleep(3)
        print(num)
    

    互斥锁

    引入锁的概念后,对于任何一个资源来说,其可以有两个状态:锁定和非锁定。

    当某个线程要更改一个数据的时候,可以先将该数据锁定,此时该数据处于锁定状态,其它的线程不能更改它,直到该线程操作完该数据,将其解锁,然后该数据就处于了非锁定状态,其它资源此时才可以使用该资源。互斥锁可以保证每次都只有一个线程执行写入操作。

    import threading
    import time
    num = 0
    
    # 创建一个锁
    lock = threading.Lock()
    
    def fun1():
        global num
        # 上锁
        lock.acquire()
        for i in range(0, 500000):
            num += 1
        # 解锁
        lock.release()
        print('fun1:%s' % num)
    
    def fun2():
        global num
        # 上锁
        lock.acquire()
        for i in range(0, 500000):
            num += 1
        # 解锁
        lock.release()
        print('fun2:%s' % num)
    
    if __name__ == '__main__':
    
        t1 = threading.Thread(target=fun1)
        t2 = threading.Thread(target=fun2)
        t1.start()
        t2.start()
    
        time.sleep(3)
        print(num)
    

    被锁的资源应该越少越好

    import threading
    import time
    num = 0
    
    # 创建一个锁
    lock = threading.Lock()
    
    def fun1():
        global num
        for i in range(0, 500000):
            # 上锁
            lock.acquire()
            num += 1
            # 解锁
            lock.release()
        print('fun1:%s' % num)
    
    def fun2():
        global num
        for i in range(0, 500000):
            # 上锁
            lock.acquire()
            num += 1
            # 解锁
            lock.release()
        print('fun2:%s' % num)
    
    if __name__ == '__main__':
    
        t1 = threading.Thread(target=fun1)
        t2 = threading.Thread(target=fun2)
        t1.start()
        t2.start()
    
        time.sleep(3)
        print(num)
    

    死锁

    在多线程操作共享资源的时候,如果两个线程分别占有一部分资源并且同时等待对方释放资源,那么此时就造成了死锁。(一般出现在多个锁的时候)

    import threading
    import time
    
    # 创建锁
    lock1 = threading.Lock()
    lock2 = threading.Lock()
    
    def fun1():
        lock1.acquire()
        print('func1 开始执行')
        time.sleep(1)
    
        lock2.acquire()
        print('func1 执行结束')
        lock2.release()
    
        lock1.release()
    
    
    def fun2():
        lock2.acquire()
        print('func2 开始执行')
        time.sleep(1)
    
        lock1.acquire()
        print('func2 执行结束')
        lock1.release()
    
        lock2.release()
    
    
    if __name__ == '__main__':
    
        t1 = threading.Thread(target=fun1)
        t2 = threading.Thread(target=fun2)
        t1.start()
        t2.start()
    

    进程(multiprocessing)

    进程:正在运行着的程序,每个进程都有自己的独立的内存区域,每个内存区域内都有自己的堆、栈、数据段、代码段,因此全局变量值在各子进程中不能共享。

    import multiprocessing
    import time
    
    def eat(sec):
        print('吃饭')
        time.sleep(sec)
        print('吃饭结束')
    
    
    def whtch(sec, other):
        print('看手机')
        time.sleep(sec)
        print(other)
        print('看手机结束')
    
    
    if __name__ == '__main__':
    
        # 创建进程对象
        p1 = multiprocessing.Process(target=eat, args=(2,))
        p2 = multiprocessing.Process(target=whtch, args=(5, '吹一波'))
        
        # 启动进程
        p1.start()
        p2.start()
    

    我们可以看到,在Python中使用进程和使用线程,使用方式十分的相似

    队列(Queue)

    from multiprocessing import Queue
    
    # 获取一个最大只能存5个数据的队列对象
    q1 = Queue(5)
    
    # 检验队列是否满了
    print(q1.empty())
    
    # 向队列中放数据
    q1.put('大哥')
    q1.put((1, 2, 3))
    q1.put(10000)
    q1.put(['帅哥', '美女'])
    q1.put({'name': '张思睿'})
    # 向队列中放数据,并且不希望等待
    q1.put_nowait({'name': '张三'})
    
    # 检验队列是否满
    print(q1.full())
    
    # 从队列中取数据
    print(q1.get())
    print(q1.get())
    print(q1.get())
    print(q1.get())
    print(q1.get())
    # 取数据,并且不希望等待
    print(q1.get_nowait())
    
    

    进程池

    使用进程池可以重复使用进程池里进程。一般用户进程数量不定或者进程数量较大的时候。

    在初始化进程池的时候,可以指定该池能存放的进程的最大数量,当有新任务需要使用进程的时候,如果该进程池没有满,那么会创建一个新的进程去执行该任务,如果进程池已满,那么该任务会等待着,等该进程池中有进程的任务结束的时候,会用该进程来执行新的任务。

    from multiprocessing import Pool
    import os, random,time
    
    # 定义一个工作函数
    def worker(xxx, yyy):
        print(xxx, random.randint(0, 100))
        # os.getpid()可以获取当前进程的id, os.getppid()可以获取当前进程的父进程id
        print(yyy, '进程id:%s' % os.getpid())
        time.sleep(1)
    
    
    if __name__ == '__main__':
    
        # 定义一个最大只能存放5个进程的进程池
        p1 = Pool(5)
    
        for i in range(0, 20):
            # 调用工作函数,第一个参数表示进程执行的函数,第二个参数表示进程执行函数需要的参数
            p1.apply_async(worker, ('哈哈哈', '呵呵呵'))
    
        print('开始')
        # 进程池必须先调用close(),才能调用join()
        p1.close()
        # 如果一个进程调用了join,表示该进程执行完以后才会执行主进程
        p1.join()
    
    

    守护进程

    守护进程会随着主进程的代码的执行完毕而结束。

    def eat(sec):
        print('吃饭')
        time.sleep(sec)
        print('吃饭结束')
    
    if __name__ == '__main__':
    
        p1 = multiprocessing.Process(target=eat, args=(2,))
        
        # 设置该子进程为守护进程
        p1.daemon = True
        p1.start()
    
    

    其它方法

    # 判断子进程是否活着
    p.is_alive()
    # 结束子进程
    p.terminate()
    # 获取进程的名字
    p.name
    
    
    
  • 相关阅读:
    格式化数字,将字符串格式的数字,如:1000000 改为 1 000 000 这种展示方式
    jquery图片裁剪插件
    前端开发采坑之安卓和ios的兼容问题
    页面消息提示,上下滚动
    可以使用css的方式让input不能输入文字吗?
    智慧农村“三网合一”云平台测绘 大数据 农业 信息平台 应急
    三维虚拟城市平台测绘 大数据 规划 三维 信息平台 智慧城市
    农业大数据“一张图”平台测绘 大数据 房产 国土 农业 信息平台
    应急管理管理局安全生产预警平台应急管理系统不动产登记 测绘 大数据 规划 科教 三维 信息平台
    地下综合管廊管理平台测绘 大数据 地下管线 三维 信息平台
  • 原文地址:https://www.cnblogs.com/imshun/p/10519001.html
Copyright © 2011-2022 走看看