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
    
    
    
  • 相关阅读:
    编程模式
    第六章类(十九)readonly
    Javascript----实现鼠标背景效果(同时不影响其操作)
    Javascript----input事件实现动态监听textarea内容变化
    javascript----mouseover和mouseenter的区别
    Javascript----实现火箭按钮网页置顶
    Javascript----scroll事件进度条监听
    Javascript----生成省-市下拉表单
    Javascript----增删改查
    javascript-----轮播图插件
  • 原文地址:https://www.cnblogs.com/imshun/p/10519001.html
Copyright © 2011-2022 走看看