zoukankan      html  css  js  c++  java
  • Day 41 线程

    进程只能在同一个时间干一件事情,如果想同时干两件或者多件事情,进程就无能为力了。

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

    一是由于进程是资源的拥有者,创建和撤销和切换存在较大的时空开销,因此需要引入轻型进程,

    因此在80年代,出现了能独立运行的基本单位---线程(Threads)

    注意:进程是资源分配的最小单位, 线程是CPU 调度的最小单位。

       每个进程中至少有一个线程。

    线程与进程的区别以下区别4点

    1. 地址空间和其他资源;进程间互相独立,同一进程的各线程间共享,某进程内的线程在其他进程不可见.

    2 .通信,进程间通信IPC ,线程间可以直接读写进程数据段来进行通信。 

    3.调度和切换,线程上下文切换比进程上下文切换要快.

    4. 在多线程操作系统中,进程不是一个可执行的实体.

    在多线程操作系统中,通常是在一个进程中包括多个线程,每个线程都是作为cpu的基本单位,是花费最小开销的实体

    线程具有以下属性:

    1. 轻型实体 。

    线程的实体包括程序,数据和tcb ,线程是动态概念,他的动态特性由线程控制块TCB 描述。

    2. 独立调度和分派的基本单位. 

    3. 共享进程资源.(不需要进行IPC间通信了)

    线程在同一进程中的各个线程,都可以共享该进程所有的资源,这表现在所有线程都具有相同的进程ID,这意味着线程可以访问该进程的每一个内存资源.由于同一个进程内的线程共享内存和文件,所以线程之间相互通信不必调用内核.

    4 .可并发执行

    在一个进程中的多个线程之间,可以并发执行,甚至运行在一个进程中所有线程都能并发执行,同样,不通进程中的线程也能并发执行,充分利用和发挥了处理机与外围并行工作的能力。

    不同的进程之间是充满敌意的,彼此是抢占、竞争cpu的关系,如果迅雷会和QQ抢资源。而同一个进程是由一个程序员的程序创建,所以同一进程内的线程是合作关系,一个线程可以访问另外一个线程的内存地址,大家都是共享的。

    1. 父进程有多个线程,那么开启的子线程是否需要同样多的线程

    2. 在同一个进程中,如果一个线程关闭了文件,而另外一个线程正准备往该文件内写内容呢?

    因此,在多线程的代码中,需要更多的心思来设计程序的逻辑、保护程序的数据。

    全局解释器锁GIL 

    全局解释器锁-GIL 同一时刻只能有一个线程访问CPU.锁的是线程.

    python解释器是python语言的问题吗 ? 

    是cpython解释器的特性. 这是python的一个弊病. 也是所有解释型语言的弊端.

     Python代码的执行由Python虚拟机(也叫解释器主循环)来控制。Python在设计之初就考虑到要在主循环中,同时只有一个线程在执行。虽然 Python 解释器中可以“运行”多个线程,但在任意时刻只有一个线程在解释器中运行。
      对Python虚拟机的访问由全局解释器锁(GIL)来控制,正是这个锁能保证同一时刻只有一个线程在运行。

      在多线程环境中,Python 虚拟机按以下方式执行:

      a、设置 GIL;

      b、切换到一个线程去运行;

      c、运行指定数量的字节码指令或者线程主动让出控制(可以调用 time.sleep(0));

      d、把线程设置为睡眠状态;

      e、解锁 GIL;

      d、再次重复以上所有步骤。
      在调用外部代码(如 C/C++扩展函数)的时候,GIL将会被锁定,直到这个函数结束为止(由于在这期间没有Python的字节码被运行,所以不会做线程切换)编写扩展的程序员可以主动解锁GIL。

      thread模块不支持守护线程,当主线程退出时,所有的子线程不论它们是否还在工作,都会被强行退出。而threading模块支持守护线程,守护线程一般是一个等待客户请求的服务器,如果没有客户提出请求它就在那等着,如果设定一个线程为守护线程,就表示这个线程是不重要的,在进程退出的时候,不用等待这个线程退出。

    线程的创建 Threading.Thread类

    一 、创建线程的方式一


    from
    threading import Thread import time def sayhi(name): time.sleep(2) print('%s say hello' %name) if __name__ == '__main__': t=Thread(target=sayhi,args=('egon',)) t.start() print('主线程')

    输出结果:

     

    二、创建线程的方式二

    from threading import Thread
    import time
    class sayhi(Thread):
        def __init__(self,name):
            super().__init__()
            self.name=name
        def run(self):
            time.sleep(2)
            print('%s say hello' %self.name)
        
    if __name__ =='__main__':
        t =sayhi('egon')
        t.start()
        print('主线程')

    输出结果

     多线程与多进程 PID的比较

    from threading import Thread
    from multiprocessing import  Process
    import  os
    def work():
        print('hello',os.getpid())
    
    if __name__ == '__main__':
        # part1:在主进程下开启多个线程,每个线程都跟主进程的pid一样.
        t1 =Thread(target=work)
        t2 =Thread(target=work)
        t1.start()
        t2.start()
        print('主线程pid',os.getpid())
        print(111)
        #part2 开启多个进程,每个进程有不通的pid
        p1 =Process(target=work)
        p2 =Process(target=work)
        p1.start()
        p2.start()
        print("主进程pid",os.getpid())

    输出结果:

     开发效率的比较

    from threading import Thread
    from multiprocessing import Process
    import os
    
    def work():
        print('hello')
    
    if __name__ == '__main__':
        #在主进程下开启线程
        t=Thread(target=work)
        t.start()
        print('主线程/主进程')
        '''
        打印结果:
        hello
        主线程/主进程
        '''
    
        #在主进程下开启子进程
        t=Process(target=work)
        t.start()
        print('主线程/主进程')
        '''
        打印结果:
        主线程/主进程
        hello
        '''

    内存数据的共享问题

    import os
    import time
    from threading import Thread
    def func(a,b):
        global g
        g = 0
        print(g,os.getpid())
    
    g = 100
    t_lst = []
    for i in range(10):
        t = Thread(target=func,args=(i,5))
        t.start()
        t_lst.append(t)
    for t in  t_lst : t.join()
    print(g)

    输出结果 

    0 14444
    0 14444
    0 14444
    0 14444
    0 14444
    0 14444
    0 14444
    0 14444
    0 14444
    0 14444
    0

    进程是最小的内存分配单位

    线程是操作系统调度的最小单位 

    线程被cpu执行了 

    进程内至少含有一个线程

    进程中可以开启多个线程.

      开启一个线程锁需要的时间远远小于开启一个进程。

    多个线程内部有自己的数据栈,数据不共享。

    全局变量在多个线程之间是共享的.

    线程守护进程

    1 .守护进程随着主进程代码执行的结束而结束. 

    2.守护线程会在主线程结束之后等待其他子线程结束而结束.

    import time
    from threading import Thread
    def func1():
        while True:
            print('*'*10)
            time.sleep(1)
    def func2():
        print('in func2')
        time.sleep(5)
    
    t = Thread(target=func1,)
    t.daemon = True  #守护线程在主线程结束之后会随着其他子线程的结束而结束。
    t.start()
    t2 = Thread(target=func2,)
    t2.start()
    t2.join()
    print('主线程')

    输出结果:

    **********
    in func2
    主线程
    **********
    **********
    **********
    **********
    **********

    Join方法 :主线程一直等待全部的子线程结束之后,主线程自身才结束,程序退出。

    join所完成的工作就是线程同步,即主线程任务结束之后,进入阻塞状态,一直等待其他的子线程执行结束之后,主线程在终止,

    import time
    from threading import Thread
    def func1():
        while True:
            print('*'*10)
            time.sleep(1)
    def func2():
        print('in func2')
        time.sleep(5)
    
    t = Thread(target=func1,)
    t.daemon = True
    t.start()
    t2 = Thread(target=func2,)
    t2.start()
    t2.join()
    print('主线程')

    输出结果 

    D:PycharmProjects	estvenvScriptspython.exe "D:/parcharm/12/Day 30/2.py"
    **********
    in func2
    **********
    **********
    **********
    **********
    **********
    主线程
    

      

    线程Lock (互斥锁) 只有一把钥匙

    线程的数据是共享的所以,一般我们会上锁,而 进程一般不会上锁锁,因为数据本身是隔离的。

    # noodle_lock  = Lock()
    # fork_lock = Lock()
    # def eat1(name):
    #     noodle_lock.acquire()
    #     print('%s拿到面条啦'%name)
    #     fork_lock.acquire()
    #     print('%s拿到叉子了'%name)
    #     print('%s吃面'%name)
    #     fork_lock.release()
    #     noodle_lock.release()
    #
    # def eat2(name):
    #     fork_lock.acquire()
    #     print('%s拿到叉子了'%name)
    #     time.sleep(1)
    #     noodle_lock.acquire()
    #     print('%s拿到面条啦'%name)
    #     print('%s吃面'%name)
    #     noodle_lock.release()
    #     fork_lock.release()
    #
    # Thread(target=eat1,args=('alex',)).start()
    # Thread(target=eat2,args=('Egon',)).start()
    # Thread(target=eat1,args=('bossjin',)).start()
    # Thread(target=eat2,args=('nezha',)).start()

    输出结果 

    D:PycharmProjects	estvenvScriptspython.exe "D:/parcharm/12/Day 30/2.py"
    alex拿到面条啦
    alex拿到叉子了
    alex吃面
    Egon拿到叉子了
    bossjin拿到面条啦

    递归锁 RLOCK (递归锁.)解决死锁问题. 

    from threading import RLock   # 递归锁
    fork_lock = noodle_lock  = RLock()   # 一个钥匙串上的两把钥匙
    def eat1(name):
        noodle_lock.acquire()            # 一把钥匙
        print('%s拿到面条啦'%name)
        fork_lock.acquire()
        print('%s拿到叉子了'%name)
        print('%s吃面'%name)
        fork_lock.release()
        noodle_lock.release()
    
    def eat2(name):
        fork_lock.acquire()
        print('%s拿到叉子了'%name)
        time.sleep(1)
        noodle_lock.acquire()
        print('%s拿到面条啦'%name)
        print('%s吃面'%name)
        noodle_lock.release()
        fork_lock.release()
    
    Thread(target=eat1,args=('alex',)).start()
    Thread(target=eat2,args=('Egon',)).start()
    Thread(target=eat1,args=('bossjin',)).start()
    Thread(target=eat2,args=('nezha',)).start()
    

      

    信号量和事件Event

    信号量 :同一时间允许多个线程访问一段数据。

    import time
    from threading import Semaphore,Thread
    def func(sem,a,b):
        sem.acquire()
        time.sleep(1)
        print(a+b)
        sem.release()
    
    sem = Semaphore(4)
    for i in range(10):
        t = Thread(target=func,args=(sem,i,i+5))
        t.start()

    输出结果:

    7
    9
    5
    11
    13
    15
    17
    19
    21
    23
    同时输出4个数据。

    事件

    # 事件被创建的时候
    # False状态
    # wait() 阻塞
    # True状态
    # wait() 非阻塞
    # clear 设置状态为False
    # set 设置状态为True

    # 数据库 - 文件夹
    # 文件夹里有好多excel表格
    # 1.能够更方便的对数据进行增删改查
    # 2.安全访问的机制


    # 起两个线程
    # 第一个线程 : 连接数据库
    # 等待一个信号 告诉我我们之间的网络是通的
    # 连接数据库
    # 第二个线程 : 检测与数据库之间的网络是否连通
    # time.sleep(0,2) 2
    # 将事件的状态设置为True

    import time
    import random
    from threading import Thread,Event
    def connect_db(e):
        count = 0
        while count < 3:
            e.wait(0.5)   # 状态为False的时候,我只等待1s就结束
            if e.is_set() == True:
                print('连接数据库')
                break
            else:
                count += 1
                print('第%s次连接失败'%count)
        else:
            raise TimeoutError('数据库连接超时')
    
    def check_web(e):
        time.sleep(random.randint(0,3))
        e.set()
    
    e = Event()
    t1 = Thread(target=connect_db,args=(e,))
    t2 = Thread(target=check_web,args=(e,))
    t1.start()
    t2.start()
    

     条件condition(更为复杂的锁.)

    acquire release  notify wait 四种方法.

    一个条件被创建初,默认有一个false

     notify 和wait 必须在release 和acquire 之间。

    # 条件
    from threading import Condition
    
    # 条件
    # 锁
    # acquire release
    # 一个条件被创建之初 默认有一个False状态
    # False状态 会影响wait一直处于等待状态
    # notify(int数据类型)  造钥匙
    from threading import Thread,Condition
    def func(con,i):
        con.acquire()
        con.wait() # 等钥匙
        print('在第%s个循环里'%i)
        con.release()
    con = Condition()
    for i in range(10):
        Thread(target=func,args = (con,i)).start()
    while True:
        num = int(input('>>>'))
        con.acquire()
        con.notify(num)  # 造钥匙
        con.release()
    

      定时器 Timer)

    import time
    from threading import Timer
    def func():
        print('时间同步')   #1-3
    
    while True:
        t = Timer(5,func).start()   # 非阻塞的
        time.sleep(5)
    等待五秒执行func里的代码

      

    Python标准模块--concurrent.futures

    import time
    from concurrent.futures import ThreadPoolExecutor
    def func(n):
        time.sleep(2)
        print(n)
        return n*n
    
    def call_back(m):
        print('结果是 %s'%m.result())
    
    tpool = ThreadPoolExecutor(max_workers=5)   #  默认 不要超过cpu个数*5
    for i in  range(20):
        tpool.submit(func,i).add_done_callback(call_back)
    
    
    # tpool.map(func,range(20))  # 拿不到返回值
    # t_lst = []
    # for i in  range(20):
    #     t = tpool.submit(func,i)
    #     t_lst.append(t)
    # tpool.shutdown()  # close+join    #
    # print('主线程')
    # for t in t_lst:print('***',t.result())
    
    # ftp
    # 并发编程
    

      

    队列

    Queue

    q =queue.Queue() 队列先进先出

    import queue
    
    q=queue.Queue()
    q.put('first')
    q.put('second')
    q.put('third')
    
    print(q.get())
    print(q.get())
    print(q.get())
    

      输出结果.

    first
    second
    third
    

      

    q = queue.LifoQueue() 栈 ,先进后出

    import queue
    
    q=queue.LifoQueue()
    q.put('first')
    q.put('second')
    q.put('third')
    
    print(q.get())
    print(q.get())
    print(q.get())
    '''
    结果(后进先出):
    third
    second
    first
    '''
    

    优先级多列 

    q= queue.PriorityQueue()

    import queue
    q =queue.PriorityQueue()
    q.put((20,'a'))
    q.put((10,'b'))
    q.put((30,'c'))
    q.put((-5,'d'))
    q.put((1,'?'))
    print(q.get())
    

    结果:

    D:PycharmProjects	estvenvScriptspython.exe "D:/parcharm/12/Day 30/2.py"
    (-5, 'd')
    

      

  • 相关阅读:
    获取本地IP地址
    c#从服务器下载文件代码
    Jquery 树控件(Jquery)
    Request.ServerVariables 参数大全
    Developing for App StoreBuilding an App for the App Store02
    App Store1.11
    Basic Tasks1.6
    Design Patterns1.8
    Frameworks1.7
    App Design1.10
  • 原文地址:https://www.cnblogs.com/mengbin0546/p/8674713.html
Copyright © 2011-2022 走看看