线程入门

一、线程

初识别线程

在传统操作系统中,每个进程有一个地址空间,而且默认就有一个控制线程
在工厂中, 每个车间都有房子,而且每个车间默认就有一条流水线.

操作系统 ---> 工厂
进程 ---> 车间
线程 ---> 流水线
cpu ---> 电源

线程:cpu最小的执行单位
进程:资源集合/资源单位.
线程运行 = 运行代码
进程运行 = 各种资源 + 线程

右键运行程序:申请内存空间,先把解释器丢进去并且把代码丢进去(进程做的),运行代码(线程)

进程和线程的区别

过程描述的区别:

  • 线程 --> 单指代码的执行过程

  • 进程--> 资源的申请与销毁的过程

内存空间的却别:

  • 进程内存空间彼此隔离

  • 同一个进程下的超线程共享资源

进程和线程的创建速度:

  • 进程需要是年轻资源开辟空间:慢
  • 线程只是告诉操作系统一个执行方案:快

二、线程开启的两种方式

方式一:

from threading import Thread

def task():
    print('子线程...')
    
if __name__ == '__main__':
    t = Thread(target=task)
    t.start()
    print('主')

方式二:

from threading import Thread

class Task(Thread):
    def run(self):
        pass
    
if __name__ == '__main__':
	t = Task()
    t.start()
    print('主')

三、子线程和子进程的创建速度

进程和线程的创建速度

开启子进程需要申请资源开辟空间: 慢

开启子线程只是告诉操作系统一个执行方案: 快

开启子线程的打印效果:

# 子线程 is running
# 主
# 子线程 is end

开启子进程打印效果:

# 主
# 子进程 is running
# 子进程 is end

代码:

from threading import Thread
from multiprocessing import Process
import time

def task(name):
    print(f'{name} is running')
    time.sleep(2)
    print(f'{name} is end')
    
if __name__ == '__main__':
    t = Thread(target=task, args=('子线程',))
    p = Process(target=task, args=('子进程',))
    # t.start()
    p.start()
    print('主')

四、子线程共享资源

from threading import Thread
import time, os

x = 100
def task():
    global x
    x = 50
    print(os.getpid())  # 5204
    
if __name__ == '__main__':
    t = Thread(target=task)
    t.start()
    time.sleep(2)
    print(x)  # 50
    print(os.getpid()) # 5204

五、线程的join方法

from threading import Thread
import time

def task(name, n):
    print(f'{name} start')
    time.sleep(n)
    print(f'{name} end')
    
if __name__ == '__main__':
    t1 = Thread(target=task, args=('线程1', 1))
    t2 = Thread(target=task, args=('线程2', 2))
    t3 = Thread(target=task, args=('线程3', 3))
    start = time.time()
    t1.start()
    t2.start()
    t3.start()
    t1.join() # 1s
    t2.join() # 2s
    t3.join() # 3s
    end = time.time() # 3.0039877891540527
    print(end - start)

六、了解进程的join

from multiprocessing import Process
from threading import Thread
import time

def task():
	print('进程 开启')
    time.sleep(10)
    print('进程 结束')
    
def taks2():
    print('子线程 开启')
    time.sleep(2)
    print('子线程 结束')
    
if __name__ == '__main__':
    p = Process(target=task)
    t = Thread(target=task2)
    t.start() # 开线程
    p.start() # 开进程
    print('子进程join开始')
    p.join() # 主进程的主线程等待子进程运行结束
    print('主')

七、线程相关的其他方法(了解)

# Thread实例对象的方法
isAlive():  # 返回线程是否活动的。
getName():  # 返回线程名。
setName():  # 设置线程名
    
threading模块提供的一些方法:
threading.currentThread():  # 返回当前的线程变量
threading.enumerate():  # 返回一个包含正在运行的线程的list,正在运行指线程启动后、结束							前,不包括启动前和终止后的线程。
threading.activeCount():  # 返回正在运行的线程数量,与len(threading.enumerate())								有相同的效果

八、守护线程(了解)

守护线程首先是一个线程

  • 守护线程守护到当前进程运行结束。
  • ps:比如有未完成的子进程阶段会守护,比如有未完成的其他子线程也均会守护。

守护进程首先是一个进程

  • 守护进程守护到当前进程的最后一行代码结束。
# 守护线程守护到当前进程结束
from threading import Thread
import threading
import time

def threadtask(name):
    print(f'{name} start')
    print(time.sleep(20))
    print(f'{name} end')
    
def threadtask2(name):
    print(f'{name} start')
    time.sleep(10)
    print(threading.enumerate()) # [<_MainThread(MainThread, stopped 14544)>, <Thread(Thread-1, started daemon 13676)>, <Thread(Thread-2, started 13148)>]
    print(f'{name} end')
    
if __name__ == '__main__':
    t = Thread(target=threadtask, args=('守护线程',))
    t2 = Thread(target=threadtask2, args=('子线程',))
    t.daemon = True
    t.start()
    t2.start()
    print('主')
    '''
    守护线程  start
    子线程 start
    主
    [<_MainThread(MainThread, stopped 15448)>, <Thread(Thread-1, started daemon 17520)>, <Thread(Thread-2, started 10356)>]
    子线程 end

    '''
    #  可以看到当主线程已经结束的时候,其他子线程没有结束的时候打印当前的活跃的线程发现有		守护线程。

九、GIL全局解释器锁

​ Python代码的执行由Python虚拟机(也叫解释器主循环)来控制。Python在设计之初就考虑到要在主循环中,同时只有一个线程在执行。虽然Python解释器中可以"运行"多个线程,但在任意时刻只有一个线程在解释器中运行。

​ 对Python虚拟机的访问由全局解释器锁(GIL)来控制,正是这个锁能保证同一时刻只有一个线程在运行。在多线程环境中,Python虚拟机按以下方式执行:

  1. 设置GIL;
  2. 切换到一个线程去运行;
  3. 运行指定数量的字节码指令或者线程主动让出控制(可以调用 time.sleep(0));
  4. 把线程设置为睡眠状态;
  5. 解锁GIL;
  6. 再次重复以上所有步骤。

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

原文地址:https://www.cnblogs.com/17vv/p/11535044.html