线程基础

线程基础

一、线程和进程的区别

  • 进程:
    • 进程之间数据隔离,每一个进程分配一块内存

    • 进程可以使用多核处理,操作系统调度

    • 进程是资源计算机资源份分配的最小单位

    • 进程有数据不安全的问题,需要用到锁和信号量

    • 进程之间本身无法通信,但是可以使用第三方模块和工具来实现通信IPC

      • 队列:基于socket、pickle和lock来实现,队列的数据因为锁机制是安全的
      • 管道:基于socket和pickle来实现,数据是不安全的
      • 第三方工具:已开发完成,可以直接使用,稳定安全,工作中基本上都是使用第三方工具
    • 进程之间可以通过Manager模块实现数据的共享,也被称为内存共享

    • 进程开启的、关闭和切换对资源的消耗很大

    • 一般情况下进程开启最多不要超过CPU核数的两倍,最佳的数量是CPU核数+1,CPU核数:os.cpu_count()获取

  • 线程:
    • 线程在进程之内
    • 能被操作系统调度给CPU执行的最小单位
    • 线程之间数据本身是可以共享的,但是也因此数据变得不安全
    • 同一个进程中的多个线程是可以被CPU同时执行的,可以利用多核
    • 线程开启、关闭和切换对资源的消耗很小,速度很快

二、CPython的GIL锁

在CPython程序中gc来负责处理线程监控工作,当一个线程执行的时候,gc会对该线程进行监控,但是当一个进程中多个线程同时工作的时候,gc机制无法很好的对每一个线程进行监控,为了能够监控每一个线程,在CPython中引入了锁机制,虽然是多线程,但是CPU在调度的时候通过锁机制来控制数量,每次只能有一个线程可以获得钥匙,其他线程等待该线程执行完后,才能获取钥匙继续执行,这样gc可以监控到每一个线程,虽然是多核,但是执行的结果却是单核并发,不是多核并行。这里的锁被成为全局解释器锁(GIL:global
interpreter Lock)

需要特别注意的是:线程虽然加了GIL锁机制,但不是绝对安全的,当运算量非常大的时候,GIL锁会被释放掉,这样就可能造成多线程抢占CPU导致数据不安全的问题,所以当面临很大的运算的时候需要用到多进程。虽然被称为多线程,但是因为GIL全局解释器锁在python中的多线程是伪多线程。

线程的开启

  • 线程的开启方式和进程完全一样,只不过引用的模块不同,两种方式:直接实例化和继承方式
  • 模块导入:from threading import Thread
from threading import Thread
import time
import random

def func(i):
    print('start%s' % i)
    time.sleep(random.random())
    # 阻塞
    print('end%s' % i)


if __name__ == '__main__':
    t_list = []
    for i in range(10):
        t = Thread(target=func, args=(i,))
        t.start()
        t_list.append(t)
    [t.join() for t in t_list]
    # 阻塞,异步变为同步
    print('执行结束')
  • 线程不能从外部关闭,只能是自己执行完代码之后再关闭,所以没有terminate方法
  • 线程号:t.ident来获取,每一个线程的线程号不一样,但是一个进程中的所有线程获取到的进程号是一样的。
  • 进程中可以在子进程里获取每一个子进程的pid,同样的子线程也可以通过current_thread()获取子线程的对象,通过对象获取线程号,每一个进程中都有唯一的一个主线程,无论是否开启子线程,主线程都存在。 from threading import current_thread
  • 获取所有的线程对象:enumerate(),列表类型,线程对象中除了开启的子线程,必须加上一个主线程,即开启10个子线程,获取的是11个线程
for i in range(10):
  t = Thread(target=func)
  t.start()
print(enumerate())

'''结果是:
[<_MainThread(MainThread, started 139753402050368)>, <Thread(Thread-1, started 139753362441984)>, <Thread(Thread-2, started 139753354049280)>, <Thread(Thread-3, started 139753345656576)>, <Thread(Thread-4, started 139753135535872)>, <Thread(Thread-5, started 139753127143168)>, <Thread(Thread-6, started 139753118750464)>, <Thread(Thread-7, started 139753110357760)>, <Thread(Thread-8, started 139753101965056)>, <Thread(Thread-9, started 139753093572352)>, <Thread(Thread-10, started 139753085179648)>]

'''
  • 获取开启线程的总数:active_count(),开启线程数+主线程

  • 线程之间可以直接进行数据的共享

  • 线程之间的通信用到的是queue.Queue模块,这一点和进程不同,进程是直接从multiprocessing中直接导入的。

posted @ 2020-03-10 11:51  大道至诚  阅读(111)  评论(0编辑  收藏  举报