zoukankan      html  css  js  c++  java
  • 线程基本内容

    1.线程:

      能被操作系统调度(给cpu执行)的最小单位
      同一个进程中的多个线程可以同时被CPU执行
      数据共享、可以利用多核;数据不安全
      开启关闭切换时间开销小
      在CPython中的多线程
      gc:垃圾回收机制
      引用计数 + 分代回收
      全局解释器锁的出现主要是为了完成gc的回收机制,对不同线程的引用计数的变化记录更加准确
      GIL:global interpreter lock
      导致了同一个进程中的多个线程只能有一个线程真正被cpu执行

      节省的是io操作的时间,而不是cpu计算的时间;因为cpu计算速度非常快,我们没有办法把一条进程中的所有io操作都规避掉
      JPython中: 垃圾回收机制不同;能利用多核

    2.开启线程

    import os
    from threading import Thread, current_thread, enumerate, active_count
    import time
    
    
    def func(i):
    
        print(f'start{i}', current_thread().ident)
        time.sleep(1)
        print(f'end{i}')
    
    
    if __name__ == '__main__':
    
        t_ls = []
        for i in range(10):
            t = Thread(target=func, args=(i, ))
            t.start()
    
            print(t.ident, os.getpid())
    
            t_ls.append(t)
    
        print(enumerate(), active_count())
    
        for t in t_ls: t.join()
    
        print('所有的线程都执行完了')
    
    
    """
    线程是不能从外部terminate的
    所有的子线程只能是自己执行完代码之后就关闭
    
    current_thread 获取当前代码所在的线程的对象,通过.ident获取线程的id
    
    enumerate 列表,存储了所有活着的线程对象,包括主线程
        active_count就是这个列表的长度
    
    """

    3.开启线程的另一种方法

    ading import Thread
    
    
    class MyThread(Thread):
        def __init__(self, a, b):
            self.a = a
            self.b = b
            super().__init__()
    
        def run(self):
            print(self.ident)
    
    
    t = MyThread(1, 2)
    t.start()  # 开启线程 才在线程中执行run方法
    
    print(t.ident)

    4.线程中的数据共享

    n = 100
    
    
    def func():
        global n
        n -= 1
    
    
    t_ls = []
    for i in range(100):
        t = Thread(target=func)
        t.start()
        t_ls.append(t)
    
    for t in t_ls:
        t.join()
    
    print(n)

    5.守护线程

    import time
    from threading import Thread
    
    
    def son():
        while True:
            print('in son')
            time.sleep(1)
    
    
    def son2():
        for i in range(3):
            print('in son2 ****')
            time.sleep(1)
    
    
    # t1 = time.time()
    
    t = Thread(target=son)
    t.daemon = True
    t.start()
    
    # t2 = time.time()
    # print(t2-t1)
    
    Thread(target=son2).start()
    
    
    """
    主线程会等待子线程结束之后才结束;因为主线程线束进程就会结束
    
    守护线程会随着主线程的结束而结束,即等待其他子线程都结束后才结束
    
    其他子线程 --> 主线程结束 --> 主进程结束 --> 整个进程中的所有资源被回收 --> 守护线程也被回收
    
    """

    6.线程中数据不安全的现象

    from threading import Thread
    import dis
    import time
    
    
    # n = 0
    # def add():
    #     for i in range(200000):
    #         global n
    #         n += 1
    #
    #
    # def sub():
    #     for i in range(200000):
    #         global n
    #         n -= 1
    #
    #
    # t1 = Thread(target=add)
    # t1.start()
    #
    # t2 = Thread(target=sub)
    # t2.start()
    #
    # t1.join()
    # t2.join()
    #
    # print(n)
    
    
    # a = 0
    # def func():
    #     global a
    #     a += 1
    #
    #
    # dis.dis(func)
    """
    += -= /= while if 等都是数据不安全  + 和=赋值是分开的两个操作
    append pop 数据安全 列表中的方法或字典中的方法去操作全局变量时,数据是安全的
    一般单线程不会出现数据安全的问题
    
    33            0 LOAD_GLOBAL              0 (a)
                  2 LOAD_CONST               1 (1)
                  4 INPLACE_ADD
                如果在存储新的a值之前恰好GIL锁轮转了,就会造成数据不安全的问题
                  6 STORE_GLOBAL             0 (a)
                  8 LOAD_CONST               0 (None)
                 10 RETURN_VALUE
    
    """
    n = []
    def add():
        for i in range(500000):
            n.append(1)
    
    
    def sub():
        for i in range(500000):
            if not n:
                time.sleep(0.0000001)
            n.pop()
    
    
    t_ls = []
    for i in range(20):
        t1 = Thread(target=add)
        t1.start()
    
        t2 = Thread(target=sub)
        t2.start()
    
        t_ls.append(t1)
        t_ls.append(t2)
    
    for t in t_ls:
        t.join()
    
    print(n)

    7.线程锁

    from threading import Thread, Lock
    import time
    
    
    # n = 0
    # def add(lock):
    #     for i in range(200000):
    #         global n
    #         with lock:
    #             n += 1
    #
    #
    # def sub(lock):
    #     for i in range(200000):
    #         global n
    #         with lock:
    #             n -= 1
    #
    #
    # lock = Lock()
    #
    # t1 = Thread(target=add, args=(lock, ))
    # t1.start()
    #
    # t2 = Thread(target=sub, args=(lock, ))
    # t2.start()
    #
    # t1.join()
    # t2.join()
    #
    # print(n)
    
    
    n = []
    def add():
        for i in range(500000):
            n.append(1)
    
    
    def sub(lock):
        for i in range(500000):
            with lock:
                if not n:  # 判断列表是否为空
                    time.sleep(0.0000001)  # 堵塞,强制cpu轮转
                n.pop()
    
    
    lock = Lock()
    t_ls = []
    for i in range(20):
        t1 = Thread(target=add)
        t1.start()
    
        t2 = Thread(target=sub, args=(lock, ))
        t2.start()
    
        t_ls.append(t1)
        t_ls.append(t2)
    
    for t in t_ls:
        t.join()
    
    print(n)

    8.单例模式

    from threading import Thread, Lock
    import time
    
    
    class A:
        __instance = None
        lock = Lock()
    
        def __new__(cls, *args, **kwargs):
            with cls.lock:
                if not cls.__instance:
                    time.sleep(0.0000001)  # 这里cpu轮转可能会造成单例模式失效,所以需要加锁
                    cls.__instance = super().__new__(cls)
    
                return cls.__instance
    
    
    def func():
        a = A()
        print(a)
    
    
    lock = Lock()
    
    for i in range(10):
        Thread(target=func).start()

    9.递归锁

    from threading import Lock, RLock, Thread
    
    
    """
    Lock:互斥锁
        效率较高
    
    
    RLock:递归锁
        在同一个线程中可以连续acquire多次,但是必须以相同次数的release
    
    """
    # lock = Lock()
    # with lock:
    #     pass
    #
    #
    # rlock = RLock()
    # with rlock:
    #     pass
    
    
    def func(i, rlock):
        rlock.acquire()
        rlock.acquire()
        print(i, ': start')
        rlock.release()
        print(i, ': end')
        rlock.release()
    
    
    rlock = RLock()
    for i in range(5):
        Thread(target=func, args=(i, rlock)).start()

    10.死锁现象

    """
    死锁现象是怎么产生的?
        多把锁,不管是互斥锁还是递归锁 并且在多个线程中 交叉使用
    
    如果是互斥锁,出现了死锁现象,最快速的解决方案把所有的互斥锁都改成同一把递归锁
        但是程序的效率会降低
    
    一般情况下,一把互斥锁就足够了
    
    """
    from threading import Lock, RLock, Thread
    import time
    
    
    noodle_lock = Lock()
    fork_lock = Lock()
    
    # noodle_lock = fork_lock = RLock()
    
    
    def eat(name):
        noodle_lock.acquire()
        print(f'{name}抢到面了')
    
        fork_lock.acquire()
        print(f'{name}抢到叉子了')
        print(f'{name}可以开始吃面了')
    
        time.sleep(0.0001)
    
        fork_lock.release()
        print(f'{name}放下了叉子')
    
        noodle_lock.release()
        print(f'{name}放下了面')
    
    
    def eat2(name):
        fork_lock.acquire()
        print(f'{name}抢到叉子了')
    
        noodle_lock.acquire()
        print(f'{name}抢到面了')
        print(f'{name}可以开始吃面了')
    
        noodle_lock.release()
        print(f'{name}放下了面')
    
        fork_lock.release()
        print(f'{name}放下了叉子')
    
    
    Thread(target=eat, args=('alex', )).start()
    Thread(target=eat2, args=('wusir', )).start()
    Thread(target=eat, args=('日杂', )).start()
    Thread(target=eat2, args=('大黑', )).start()

    11.队列

    import queue  # 线程之间数据安全的容器队列   原理:加锁 + 链表
    
    
    # q = queue.Queue(4)  # fifo 先进先出的队列
    #
    # for i in range(4):
    #     q.put(i)
    
    # q.put(5)  # 这里会堵塞,因为上面队列中只允许存储4个值
    # q.put_nowait(5)  # 几乎不用
    
    # for i in range(4):
    #     print(q.get())
    #
    # print(q.get())  # 这里会堵塞,因为队列中的值已被取完
    
    
    # try:
    #     q.get_nowait()
    # except queue.Empty:  # 不是内置的错误类型,而是queue模块中的错误
    #     print("队列为空")
    
    #
    # from queue import LifoQueue  # last in first out 后进先出 栈
    #
    #
    # lq = LifoQueue()
    # lq.put(1)
    # lq.put(2)
    # lq.put(3)
    #
    # for i in range(3):
    #     print(lq.get())
    
    
    from queue import PriorityQueue  # 优先级队列,根据放入数据的ascii码来从小到大输出
    
    
    priq = PriorityQueue()
    priq.put((2, 'alex'))
    priq.put((1, 'wusir'))
    priq.put((3, 'lgq'))
    
    for i in range(3):
        print(priq.get())
  • 相关阅读:
    随想24:中国终将发展成第一强国
    随想23:所见的并不一定是真实
    工作4年之后对人性、社会的疯言
    随想22:出路
    开源视频会议bigbluebutton开发(3)——架构体系图
    开源视频会议bigbluebutton开发(2)——配置命令工具
    开源视频会议bigbluebutton开发(1)——初始化安装以及配置
    视频会议之BigBlueButton
    26款 网络会议/视频会议开源软件
    Tomcat 7最大并发连接数的正确修改方法
  • 原文地址:https://www.cnblogs.com/GOD-L/p/13781736.html
Copyright © 2011-2022 走看看