zoukankan      html  css  js  c++  java
  • 线程

     

     GIL:全局解释器锁,
    #     作用:只允许一个线程通过,所有Python中的多线程是假的
    #   进程:系统分配的一个资源单位
    #   线程:进程中的一个分支,进程中至少有一个主线程
    #   多线程: 多个线程并发的一种技术
    #   同步:按顺序执行
    #   异步:可以理解为在不同的线程中独立执行
    #   并行:任务数 <= CPU数
    #   并发: 任务数 > CPU数
    #
    #   线程锁/互斥锁: threading.Lock,解决线程冲突
    #   死锁:多个线程同一时刻占用资源不愿意释放,又想占用对方的资源,造成相互等待的现象
    #   递归锁:threading.RLock, 解决死锁
    #
    #   信号量:控制线程的最大并发数

     

    线程

    在一个进程的内部,要同时干多件事,就需要同时运行多个“子任务”,我们把进程内的这些“子任务”叫做线程。

    线程通常叫做轻型的进程。线程是共享内存空间的并发执行的多任务,每一个线程都共享一个进程的资源。

    线程是最小的执行单元,而进程由至少一个线程组成。如何调度进程和线程,完全由操作系统决定,程序自己不能决定什么时候执行,执行多长时间。

    模块 1、_thread模块 低级模块,接近底层。 2、threading模块 高级模块,对_thread进行了封装

    线程基本用法

    基本跟进程类似。

    from threading import Thread, current_thread
    import time

    def run(num):
       print('子线程(%s)开始' % (current_thread().name,))
       time.sleep(2)
       print('打印', num)
       time.sleep(2)
       print('子线程(%s)结束' % (current_thread().name,))


    if __name__ == '__main__':
       # 任何进程默认就会启动一个线程,称为主线程,主线程可以启动新的子线程
       # current_thread():返回返回当前线程的实例
       print('主线程(%s)开始' % (current_thread().name,))
       # 创建子线程
       t = Thread(target=run, args=(1,), name='runThread')
       t.start()
       # 等待线程结束
       t.join()
       print("主线程(%s)结束" % (current_thread().name))

    练习: 将上述新建线程的代码改成使用继承Thread类的方式。

    常见方法

    方法名说明
    isAlive() 返回线程是否在运行。正在运行指启动后、终止前。
    get/setName(name) 获取/设置线程名。
    start() 线程准备就绪,等待CPU调度
    is/setDaemon(bool) 获取/设置是守护线程(默认前台线程(False))。(在start之前设置)
    join([timeout]) 阻塞当前上下文环境的线程,直到调用此方法的线程终止或到达指定的timeout(可选参数)

    线程间共享数据

    线程数据是共享的。先来看一个简单的例子。

    from threading import Thread
    from time import sleep


    # 全局数据
    num = 100


    def run():
       print('子线程开始')
       global  num
       num += 1
       print('子线程结束')


    if __name__ == '__main__':
       print('主线程开始')
       # 创建主线程
       t = Thread(target=run)
       t.start()
       t.join()
       print(num)
       print('主线程结束')

    执行结果:

    主线程开始
    子线程开始
    子线程结束
    101
    主线程结束

    可以看到子线程修改的全局变量,在主线程中也体现出来了。

    线程之间数据共享引发的问题

    多线程和多进程最大的不同在于,多进程中,同一个变量,各自有一份拷贝存在每个进程中,互不影响。而多线程中,所有变量都由所有线程共享。所以,任何一个变量都可以被任意一个线程修改,因此,线程之间共享数据最大的危险在于多个线程同时修改一个变量,容易把内容改乱了。

    下面来模拟这个问题:

    from threading import Thread


    # 全局变量
    num = 100

    def run(n):
       global num
       for i in range(100000000):
           num = num + n
           num = num - n


    if __name__ == '__main__':
       t1 = Thread(target=run, args=(6,))
       t2 = Thread(target=run, args=(9,))
       t1.start()
       t2.start()
       t1.join()
       t2.join()
       print('num=',num)

    运行之后发现num不等于100了。这就是因为两个线程都对num进行操作,中间发生了紊乱。

    使用线程锁解决数据混乱问题

    threading中的Lock类表示锁。

    锁确保了这段代码只能由一个线程从头到尾的完整执行。阻止了多线程的并发执行,包含锁的某段代码实际上只能以单线程模式执行,所以效率大大滴降低了。

    由于可以存在多个锁,不同线程持有不同的锁,并试图获取其他的锁,可能造成死锁,导致多个线程挂起。只能靠操作系统强制终止。

    我们给之前的代码加上锁 来解决数据混乱的问题。

    from threading import Thread, Lock


    # 全局变量
    num = 100
    # 锁对象
    lock = Lock()

    def run(n):
       global num
       global lock
       for i in range(100000000):
           # 获取锁
           lock.acquire()
           try:
               num = num + n
               num = num - n
           finally:
               # 修改完一定 要释放锁
               lock.release()


    if __name__ == '__main__':
       t1 = Thread(target=run, args=(6,))
       t2 = Thread(target=run, args=(9,))
       t1.start()
       t2.start()
       t1.join()
       t2.join()
       print('num=',num)

    使用上下文管理器 with,可以自动获取锁,释放锁。可以将上面代码的try语句改成如下:

    with lock:
    num = num + n
    num = num - n

     

  • 相关阅读:
    java web使用WebSocket
    solr的使用
    mybatis中模糊查询的实现方式
    Java反射的简单使用
    TCP/IP协议 资料整理
    Java单向链表的实现
    Spring boot使用JWT来做接口登录验证
    sql性能优化
    前端实践项目(四)使用koa搭建开发环境
    前端实践项目 目录
  • 原文地址:https://www.cnblogs.com/Deaseyy/p/10859751.html
Copyright © 2011-2022 走看看