线程 threading模块

线程

什么是线程 

  • 线程是计算机中被cpu调度的最小单位
  • cpu都是执行的线程中的代码
  • 线程被包含在进程中 ,是进程的实际运作单位
60年代,在OS中能拥有资源和独立运行的基本单位是进程,然而随着计算机技术的发展,进程出现了很多弊端,一是由于进程是资源拥有者,创建、撤消与切换存在较大的时空开销,因此需要引入 轻型进程;二是由于对称多处理机(SMP)出现, 可以满足多个运行单位,而多个进程并行开销过大。
  因此在80年代,出现了 能独立运行的基本单位——线程(Threads)
  注意:进程是资源分配的最小单位,线程是CPU调度的最小单位.
     每一个进程中至少有一个线程。 

线程和进程之间的关系

观看图解

线程与进程的区别可以归纳为以下4点:
  1)地址空间和其它资源(如打开文件):进程间相互独立,同一进程的各线程间共享。某进程内的线程在其它进程不可见。
  2)通信: 进程间通信 IPC,线程间可以直接读写进程数据段(如全局变量)来进行通信——需要 进程同步和互斥手段的辅助,以保证数据的一致性。
  3)调度和切换:线程上下文切换比进程上下文切换要快得多。
  4)在多线程操作系统中,进程不是一个可执行的实体。

一个进程中的多个线程能够并行么? 

  不行 因为Cpython中有一把全局解释器锁,GIL 所以所线程不能充分利用多核,

  同一时刻用一个进程中的线程只有一个能被CPU执行

  GIL锁 确实是限制了你的程序效率

  GIL锁目前是能够帮助你在线程中的切换中提高效率

内存中的线程

多个线程共享同一个进程的地址空间中的资源,是对一台计算机上多个进程的模拟,有时也称线程为轻量级的进程。

  而对一台计算机上多个进程,则共享物理内存、磁盘、打印机等其他物理资源。多线程的运行也多进程的运行类似,是cpu在多个线程之间的快速切换。

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

  类似于进程,每个线程也有自己的堆栈,不同于进程,线程库无法利用时钟中断强制线程让出CPU,可以调用thread_yield运行线程自动放弃cpu,让另外一个线程运行。

  线程通常是有益的,但是带来了不小程序设计难度,线程的问题是:

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

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

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


threading模块

 创建线程

def func(i):
    print('子线程 :',i,os.getpid())

print('主线程 :',os.getpid())
for i in range(10):
    t = Thread(target=func,args=(i,))
    t.start()

Thread实例对象的方法

  •  isAlive() :返回线程是否活动的
  • getName() : 返回线程名
  • setName() : 设置线程名
from threading import currentThread
def func():
    time.sleep(3)

t = Thread(target=func)
t.start()
print(t.is_alive())
print(t.getName())
t.setName('t1')
print(t.getName())
threading模块提供的一些方法:(不常用)
  threading.currentThread(): 返回当前的线程变量。
m threading import currentThread
def func():
    print('子线程 : ',currentThread().name) #返回线程名
    time.sleep(3)

print('主线程 : ',currentThread().ident) #返回线程地址
t = Thread(target=func)
t.start()
threading.enumerate(): 返回一个包含正在运行的线程的list。正在运行指线程启动后、结束前,不包括启动前和终止后的线程。
from threading import currentThread
from threading import enumerate
def func():
    print('子线程 : ',currentThread().ident)
    time.sleep(3)

print('主线程 : ',currentThread().ident)
for i in range(10):
    t = Thread(target=func)
    t.start()
print(len(enumerate()))
  threading.activeCount(): 返回正在运行的线程数量,与len(threading.enumerate())有相同的结果。
from threading import currentThread
from threading import activeCount
def func():
    print('子线程 : ',currentThread().ident)
    time.sleep(3)

print('主线程 : ',currentThread().ident)
for i in range(10):
    t = Thread(target=func)
    t.start()
print(activeCount())

守护线程

 无论是进程还是线程,都遵循:守护者会等待主程序运行完毕后被销毁。需要强调的是:运行完毕并非终止运行

  •  对主进程来说,运行完毕指的是主进程代码运行完毕
  • 对主线程来说,运行完毕指的是主线程所在的程序内所有的非守护线程全部运行完毕,主线程才算运行完毕
主进程在其代码结束后就已经算运行完毕了(守护进程在此时就被回收),
然后主进程会一直等非守护的子进程都运行完毕后回收子进程的资源(否则会产生僵尸进程),才会结束
主线程在其他非守护线程运行完毕后才算运行完毕(守护线程在此时就被回收)。
因为主线程的结束意味着进程的结束,进程整体的资源都将被回收,
而进程必须保证非守护线程都运行完毕后才能结束。

方法一

time
from threading import Thread
def func1():
    while True:
        time.sleep(0.5)
        print(123)

def func2():
    print('func2 start')
    time.sleep(3)
    print('func2 end')

t1 = Thread(target=func1)
t2 = Thread(target=func2)
t1.setDaemon(True)  #添加守护线程
t1.start()
t2.start()
print('主线程的代码结束')

方法二

from threading import Thread
import time
def foo():
    print(123)
    time.sleep(1)
    print("end123")

def bar():
    print(456)
    time.sleep(3)
    print("end456")


t1=Thread(target=foo)
t2=Thread(target=bar)

t1.daemon=True
t1.start()
t2.start()
print("main-------")

猜你喜欢

转载自www.cnblogs.com/qq752059037/p/9699750.html