• python基础---->进程、线程及相关等



    基本概念

    进程

    1、什么是进程?

      程序并不能单独运行,只有将程序装载到内存中,系统为它分配资源才能运行,而这种执行的程序就称之为进程。

      程序和进程的区别就在于:程序是指令的集合,它是进程运行的静态描述文本;进程是程序的一次执行活动,属于动态概念。

      简单点说就是:进程就是一个程序在一个数据集上的一次动态执行过程。

    2、进程的特点:

      一个时间点只能做一件事,不能同时干两件及以上的事;

      在执行的过程进程如果阻塞,例如等待输入,整个进程就会挂起,即使进程中有些工作不依赖于输入的数据,也将无法执行。

    线程

    1、什么是线程(Thread)?

      线程也叫轻量级进程,它是一个基本的CPU执行单元,也是程序执行过程中的最小单元

      一条线程指的是进程中一个单一顺序的控制流,一个进程中可以并发多个线程,每条线程并行执行不同的任务;

    2、线程的特点:

      突破一个进程只能干一样事的缺陷,使到进程内并发成为可能;

      线程的引入减小了程序并发执行时的开销,提高了操作系统的并发性能; 

    进程和线程的区别 

      线程是执行的指令集 , 进程是资源的集合

      线程的启动速度要比进程的启动速度要快

      两个线程的执行速度是一样的,进程与线程的运行速度是没有可比性的

      线程共享创建它的进程的内存空间 , 进程的内存是独立的

      两个线程共享的数据都是同一份数据 , 两个子进程的数据不是共享的 , 而且数据是独立的

      同一个进程的线程之间可以直接交流 , 同一个主进程的多个子进程之间是不可以进行交流 , 如果两个进程之间需要通信 , 就必须要通过一个中间代理来实现

      一个新的线程很容易被创建 , 一个新的进程创建需要对父进程进行一次克隆

      一个线程可以控制和操作同一个进程里的其他线程 , 线程与线程之间没有隶属关系 , 但是进程只能操作子进程

      改变主线程 , 有可能会影响到其他线程的行为 , 但是对于父进程的修改是不会影响子进程

    python threading模块

    线程有两种调用方式:

    直接调用:

    import threading
    import time
    
    def run(n):         #定义线程要运行的函数
        print('task',n)
        time.sleep(2)
    
    if __name__ == '__main__':
        t1 = threading.Thread(target=run,args=(1,))          #生成一个线程
        t2 = threading.Thread(target=run,args=(2,))          #生成一个另外线程
        
        t1.start()    # 启动线程
        t2.start()    # 启动另外一个线程
    
        print(t1.getName())    # 获取线程名
        print(t2.getName())
    
    print('I am main thread')                #主线程
    
    #这个进程里面有三个线程,1个主线程,t1,t2两个子线程
    #子线程和主线程是同步开启的,主线程结束后,要等子线程全部结束后,进程才会关闭

    PS:线程相关的方法:

    start        线程准备就绪,等待CPU调度
    setName      为线程设置名称
    getName      获取线程名称
    run          线程被cpu调度后自动执行线程对象的run方法
    
    # 下面会讲到
    setDaemon    将线程声明为守护线程,必须在start() 方法调用之前设setDaemon(),只要主线程完成了,不管子线程是否完成,都要和主线程一起退出
    join         逐个执行每个线程,执行完毕后继续往下执行,该方法保证当前线程执行完成后再执行其它线程,使得多线程变得无意义。

    继承式调用:

    import threading
    import time
    
    class MyThread(threading.Thread):   #继承threading,Thread模块
        def __init__(self,n):
            threading.Thread.__init__()    #继承父类
            # 或者super(MyThread, self).__init__() 
    
            self.n = n
    
        def run(self):                 #定义每个线程要运行的函数,必须用run
            print('task',self.n)
            time.sleep(2)
    
    
    if  __name__ == '__main__':
        t1 = MyThread(1)
        t2 = MyThread(2)
    
        t1.start()
        t2.start()
    
    print('I am main thread')

    jion和setDaemon:

    主线程:当一个程序启动时 , 就有一个进程被操作系统创建 , 与此同时一个线程也立刻运行 , 该线程通常叫做程序的主线程;

    子线程 : 因为程序是开始时就执行的 , 如果你需要再创建线程 , 那么创建的线程就是这个主线程的子线程;

    join应用:

    import threading
    import time
    
    def run(n):
        print("task ",n )
        time.sleep(2)
    
    start_time = time.time()
    t_objs = []    # 存线程实例
    
    for i in range(10):        #生成10个线程
        t = threading.Thread(target=run,args=("t-%s" %i ,))
        t.start()
        t_objs.append(t)  #为了不阻塞后面线程的启动,不在这里join,先放到一个列表里
    
    for t in t_objs:      #循环线程实例列表,等待所有线程执行完毕
        t.join()
    
    print("---all threads has finished...")
    print("cost:",time.time() - start_time)

    setDaemon应用:

    import threading
    import time
    
    def run(n):
        print('task',n)
        time.sleep(2)
        print('i am 子线程')     #主线程结束,setDaemon不管有没有运行完都会被销毁
    
    if __name__ == '__main__':
        t1 = threading.Thread(target=run,args=(1,))
        t2 = threading.Thread(target=run,args=(2,))
        t1.setDaemon(True)   #设置守护线程,放在start之前
        t1.start()
        t2.setDaemon(True)
        t2.start()
    
    print('I am main thread')
    
    结果:
    task 1
    task 2
    I am main thread

    线程锁

    lock:如果有多个进程对同一文件进行修改 , 就会造成错乱 , 所以我们为了保护文件数据的安全 , 就需要给其进行加锁,join为整体串行 , lock为局部串行

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

    加锁:

    import time
    import threading
    
    def addNum():
        global num #在每个线程中都获取这个全局变量
        print('--get num:',num )
        time.sleep(1)
        lock.acquire() #修改数据前加锁
        num  -=1 #对此公共变量进行-1操作
        lock.release() #修改后释放
    
    num = 100  #设定一个共享变量
    thread_list = []
    lock = threading.Lock() #生成全局锁
    for i in range(100):
        t = threading.Thread(target=addNum)
        t.start()
        thread_list.append(t)
    
    for t in thread_list: #等待所有线程执行完毕
        t.join()
    
    print('final num:', num )

    Semaphore(信号量)

    互斥锁 同时只允许一个线程更改数据,而Semaphore是同时允许一定数量的线程更改数据 ,比如食堂打饭有5个窗口,那最多只允许5个人同时打饭,后面的人只能等里面有人打完才能去打。

    import threading, time
    
    def run(n):
        semaphore.acquire()
        time.sleep(1)
        print("run the thread: %s
    " % n)
        semaphore.release()
    
    if __name__ == '__main__':
        semaphore = threading.BoundedSemaphore(5)  # 最多允许5个线程同时运行
        for i in range(20):
            t = threading.Thread(target=run, args=(i,))
            t.start()
    while threading.active_count() != 1:
        pass
    else:
        print('----all threads done---')
    semaphore

    Queue(队列)

    Queue是python标准库中的线程安全的队列(FIFO)实现,提供了一个适用于多线程编程的先进先出的数据结构,即队列,用来在生产者和消费者线程之间的信息传递

    基本FIFO队列

    FIFO即First in First Out,先进先出。Queue提供了一个基本的FIFO容器,使用方法很简单,maxsize是个整数,指明了队列中能存放的数据个数的上限。一旦达到上限,插入会导致阻塞,直到队列中的数据被消费掉。如果maxsize小于或者等于0,队列大小没有限制。

    简单的FIFO:

    import Queue
    
    q = Queue.Queue()
    
    for i in range(5):
        q.put(i)
    
    while not q.empty():
        print q.get()
    0
    1
    2
    3
    4
    结果

    LIFO队列

    LIFO即Last in First Out,后进先出。与栈的类似;

    简单的LIFO:

    import Queue
    
    q = Queue.LifoQueue()
    
    for i in range(5):
        q.put(i)
    
    while not q.empty():
        print q.get()
    4
    3
    2
    1
    0
    结果

    优先级队列

    import Queue
    import threading
    
    class Job(object):
        def __init__(self, priority, description):
            self.priority = priority
            self.description = description
            print('Job:',description)
            return
        def __cmp__(self, other):
            return cmp(self.priority, other.priority)
    
    q = Queue.PriorityQueue()
    
    q.put(Job(3, 'level 3 job'))
    q.put(Job(10, 'level 10 job'))
    q.put(Job(1, 'level 1 job'))
    
    def process_job(q):
        while True:
            next_job = q.get()
            print('for:', next_job.description)
            q.task_done()
    
    workers = [threading.Thread(target=process_job, args=(q,)),
            threading.Thread(target=process_job, args=(q,))
            ]
    
    for w in workers:
        w.setDaemon(True)
        w.start()
    
    q.join()
    Job: level 3 job
    Job: level 10 job
    Job: level 1 job
    for: level 1 job
    for: level 3 job
    for: job: level 10 job
    结果

    常用方法

    qsize()          
    #看队列大小
    
    task_done()
    # 意味着之前入队的一个任务已经完成。由队列的消费者线程调用。
    # 每一个get()调用得到一个任务,接下来的task_done()调用告诉队列该任务已经处理完毕。如果当前一个join()正在阻塞,它将在队列中的所有任务都处理完时恢复执行(即每一个由put()调用入队的任务都有一个对应的task_done()调用)。
    join() # 阻塞调用线程,直到队列中的所有任务被处理掉。只要有数据被加入队列,未完成的任务数就会增加。当消费者线程调用task_done()(意味着有消费者取得任务并完成任务),未完成的任务数就会减少。当未完成的任务数降到0,join()解除阻塞。 put(item[, block[, timeout]]) # 将item放入队列中。 # 1、如果可选的参数block为True且timeout为空对象(默认的情况,阻塞调用,无超时)。 # 2、如果timeout是个正整数,阻塞调用进程最多timeout秒,如果一直无空空间可用,抛出Full异常(带超时的阻塞调用)。 # 2、如果block为False,如果有空闲空间可用将数据放入队列,否则立即抛出Full异常其非阻塞版本为put_nowait等同于put(item, False) get([block[, timeout]]) # 从队列中移除并返回一个数据。block跟timeout参数同put方法 # 其非阻塞方法为`get_nowait()`相当与get(False) full() #判断队列是否有数据 返回bool值 empty() # 如果队列为空,返回True,反之返回False

    Event事件

    python线程的事件用于主线程控制其他线程的执行,事件主要提供了三个方法 set、wait、clear。

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

    # -*- coding:utf-8 -*-
    # @Author  : Clint
    import threading
    
    
    def do(event):
        print('start')
        
        event.wait()
        print('execute')
    
    
    event_obj = threading.Event()
    for i in range(10):
        t = threading.Thread(target=do, args=(event_obj,))
        t.start()
    
    event_obj.clear()
    inp = input('input:')
    if inp == 'true':
        event_obj.set()

    Condition(条件)

    使得线程等待,只有满足某条件时,才释放n个线程

    def condition_func():
    
        ret = False
        inp = input('>>>')
        if inp == '1':
            ret = True
    
        return ret
    
    
    def run(n):
        con.acquire()
        con.wait_for(condition_func)
        print("run the thread: %s" %n)
        con.release()
    
    if __name__ == '__main__':
    
        con = threading.Condition()
        for i in range(10):
            t = threading.Thread(target=run, args=(i,))
            t.start()

    Timer(定时器)

    from threading import Timer
     
     
    def hello():
        print("hello, world")
     
    t = Timer(1, hello)
    t.start()  # 一秒后打印

    进程池

    进程池内部维护一个进程序列,当使用时,则去进程池中获取一个进程,如果进程池序列中没有可供使用的进进程,那么程序就会等待,直到进程池中有可用进程为止。

    进程池中有两个方法:apply、apply_async

    # -*- coding:utf-8 -*-
    # @Author  : Clint
    from multiprocessing import Process, Pool
    import time
    
    
    def Foo(i):
        time.sleep(2)
        return i + 100
    
    
    def Bar(arg):
        print(arg)
    
    
    pool = Pool(5)
    # print pool.apply(Foo,(1,))
    # print pool.apply_async(func =Foo, args=(1,)).get()
    
    for i in range(10):
        pool.apply_async(func=Foo, args=(i,), callback=Bar)
    
    print('end')
    
    pool.close()
    pool.join()   # 进程池中进程执行完毕后再关闭,如果注释,那么程序直接关闭。
  • 相关阅读:
    【Mysql】日期时间格式化
    【CSS】css网页背景图片设置
    【C#】【Thread】SynchronizationContext线程间同步
    love2d--glsl03噪声
    love2d--glsl02变量和语句
    love2d--glsl01简单的渲染
    love2d 0.9发布
    工作感受
    本系列love2d示例代码错误集中整理
    一些lua代码
  • 原文地址:https://www.cnblogs.com/Utopia-Clint/p/10885551.html
Copyright © 2020-2023  润新知