进程、线程、协程

进程是对运行时程序的封装,是系统进行资源调度和分配的的基本单位,实现了操作系统的并发

线程是进程的子任务,是CPU调度和分派的基本单位用于保证程序的实时性,实现进程内部的并发

协程是一种用户态的轻量级线程,协程的调度完全由用户控制。从技术的角度来说,“协程就是你可以暂停执行的函数”。

对单核cpu来讲线程不能提高性能,只是提高cpu利用率;

1、当运行为非计算密集型job时,可能穿插着大量的IO调用,IO的一个特性就是阻塞等待,阻塞的时间消耗远大于线程切换的花费。所以利用多线程并发处理问题,切换阻塞等待结束的线程使用cpu可以提高cpu利用率。

2、当运行为计算密集型job时,IO阻塞影响较小,这时使用多线程,适得其反,反而会分走cpu部分性能降低了cpu的计算效率。

多核cpu和多cpu

多个物理CPU,CPU通过总线进行通信,效率比较低。

核CPU,不同的核通过L2 cache进行通信,存储和外设通过总线与CPU通信

进程与线程的关系

1、线程是进程的一部分,一个线程只能属于一个进程,而一个进程可以有多个线程,但至少有一个线程。

2、每个进程都有独立的代码和数据空间(程序上下文),程序之间的切换会有较大的开销;线程共享代码和数据空间,每个线程都有自己独立的运行栈和程序计数器(PC),线程之间切换的开销小。

3、在操作系统中能同时运行多个进程(程序);而在同一个进程(程序)中有多个线程同时执行(通过CPU调度,在每个时间片中只有一个线程执行)

4、系统在运行的时候会为每个进程分配不同的内存空间;而对线程而言,除了CPU外,系统不会为线程分配内存(线程所使用的资源来自其所属进程的资源),线程组之间只能共享资源。

线程与协程的关系

1、一个线程可以多个协程,一个进程也可以单独拥有多个协程。

2、线程进程都是同步机制,而协程则是异步。

3、协程能保留上一次调用时的状态,每次过程重入时,就相当于进入上一次调用的状态。

4、线程是抢占式,而协程是非抢占式的,所以需要用户自己释放使用权来切换到其他协程,因此同一时间其实只有一个协程拥有运行权,相当于单线程的能力。

5、协程并不是取代线程, 而且抽象于线程之上, 线程是被分割的CPU资源, 协程是组织好的代码流程, 协程需要线程来承载运行, 线程是协程的资源, 但协程不会直接使用线程, 协程直接利用的是执行器(Interceptor), 执行器可以关联任意线程或线程池, 可以使当前线程, UI线程, 或新建新程.。

6、线程是协程的资源。协程通过Interceptor来间接使用线程这个资源。

多线程简单应用

多线程施加锁

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
# -*-* encoding:UTF-8 -*-
# python3
# author : DK
# date   : 2022/03/01
import threading
import time
import numpy
# 定义取款机余额
bank_atm = 10000
# # 定义矩阵
# big_mat = numpy.random.rand(10000,10000)

threadLock = threading.Lock()

class Atm_Thread(threading.Thread):
    def __init__(self,threadId,name,access_list, sleep_time):
        threading.Thread.__init__(self)
        self.threadId = threadId
        self.name = name
        self.sleep_time = sleep_time
        self.access_list = access_list

    def run(self):
        print("开始存取:", self.name)
        # 获得锁,成功获得锁定后返回 True
        # 可选的timeout参数不填时将一直阻塞直到获得锁定
        # 否则超时后将返回 False
        threadLock.acquire()
        access(self.name, self.sleep_time, self.access_list)
        # matrix(self.name, self.sleep_time, self.access_list)
        # 释放锁
        threadLock.release()

    def __del__(self):
        print(self.name, "线程结束!")


def access(threadName, sleep_time, access_list):
    global bank_atm
    for i in access_list:
        time.sleep(sleep_time)
        bank_atm = bank_atm + i
        space = " "*(6 - len(str(i)))
        if i>=0:
            print("[%s] %s 存入ATM %d%s 元,存入后ATM的余额为 %d 元" % (time.ctime(time.time()),threadName,i,space,bank_atm))
        else:
            print("[%s] %s 取出ATM %d%s 元,取出后ATM的余额为 %d 元" % (time.ctime(time.time()),threadName,i,space,bank_atm))


# def matrix(threadName, sleep_time, times):
#     global big_mat
#     start = time.time()
#     for i in range(times):
#         big_mat = big_mat*big_mat
#     end = time.time()
#     cost = end - start
#     print("%s 花费时常约%d"%(threadName, cost))


threads = []
print("创建3个人进行存取款")

for i in range(3):
    name = "Thread-" + str(i)
    # 创建新线程
    thread_person = Atm_Thread(i, name, [100,-100, 500, 10000, -10000],1)
    # 开启线程
    thread_person.start()
    # 添加线程到线程列表
    threads.append(thread_person)

# 等待所有线程完成
for t in threads:
    t.join()
print("主进程结束!")

运行结果:

多线程不施加锁

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
# -*-* encoding:UTF-8 -*-
# python3
# author : DK
# date   : 2022/03/01
import threading
import time
import numpy
# 定义取款机余额
bank_atm = 10000
print("ATM当前余额为 %d 元"%bank_atm)
# # 定义矩阵
# big_mat = numpy.random.rand(10000,10000)

threadLock = threading.Lock()

class Atm_Thread(threading.Thread):
    def __init__(self,threadId,name,access_list, sleep_time):
        threading.Thread.__init__(self)
        self.threadId = threadId
        self.name = name
        self.sleep_time = sleep_time
        self.access_list = access_list

    def run(self):
        print("创建人员:", self.name)
        # 获得锁,成功获得锁定后返回 True
        # 可选的timeout参数不填时将一直阻塞直到获得锁定
        # 否则超时后将返回 False
        # threadLock.acquire()
        access(self.name, self.sleep_time, self.access_list)
        # matrix(self.name, self.sleep_time, self.access_list)
        # 释放锁
        # threadLock.release()

    def __del__(self):
        print(self.name, "线程结束!")


def access(threadName, sleep_time, access_list):
    global bank_atm
    for i in access_list:
        time.sleep(sleep_time)
        bank_atm = bank_atm + i
        space = " "*(6 - len(str(i)))
        if i>=0:
            print("[%s] %s 存入ATM %d%s 元,存入后ATM的余额为 %d 元" % (time.ctime(time.time()),threadName,i,space,bank_atm))
        else:
            print("[%s] %s 取出ATM %d%s 元,取出后ATM的余额为 %d 元" % (time.ctime(time.time()),threadName,i,space,bank_atm))


# def matrix(threadName, sleep_time, times):
#     global big_mat
#     start = time.time()
#     for i in range(times):
#         big_mat = big_mat*big_mat
#     end = time.time()
#     cost = end - start
#     print("%s 花费时常约%d"%(threadName, cost))


threads = []
print("创建3个人进行存取款")

for i in range(3):
    name = "Thread-" + str(i)
    # 创建新线程
    thread_person = Atm_Thread(i, name, [100,-100, 500, 10000, -10000],1)
    # 开启线程
    thread_person.start()
    # 添加线程到线程列表
    threads.append(thread_person)

# 等待所有线程完成
for t in threads:
    t.join()
print("主进程结束!")

运行结果:

可自行调用matrix函数检测多线程在多核处理器中的运行状况

多进程简单应用

多进程并行(非阻塞)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# -*-* encoding:UTF-8 -*-
# python3
# author : DK
# date   : 2022/03/01
#coding: utf-8
import multiprocessing
import time

def func(msg):
    print("[%s] 开始休眠 %d 秒"%(time.ctime(time.time()), msg))
    time.sleep(msg)
    print("[%s] 我休眠了 %d 秒"%(time.ctime(time.time()), msg))

if __name__ == "__main__":
    cpu_num = multiprocessing.cpu_count()
    pool = multiprocessing.Pool(processes = cpu_num)
    for i in range(4):
        pool.apply_async(func, (i, ))   #维持执行的进程总数为processes,当一个进程执行完毕后会添加新的进程进去

    print('开始休眠进程')
    pool.close()
    pool.join()   #调用join之前,先调用close函数,否则会出错。执行完close后不会有新的进程加入到pool,join函数等待所有子进程结束
    print('休眠测试结束')

运行结果:

多进程并行(阻塞)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
# -*-* encoding:UTF-8 -*-
# python3
# author : DK
# date   : 2022/03/01
#coding: utf-8
import multiprocessing
import time

def func(msg):
    print("[%s] 开始休眠 %d 秒"%(time.ctime(time.time()), msg))
    time.sleep(msg)
    print("[%s] 我休眠了 %d 秒"%(time.ctime(time.time()), msg))

if __name__ == "__main__":
    cpu_num = multiprocessing.cpu_count()
    pool = multiprocessing.Pool(processes = cpu_num)
    for i in range(4):
        pool.apply(func, (i, ))   #维持执行的进程总数为processes,当一个进程执行完毕后会添加新的进程进去
        

    print('开始休眠进程')
    pool.close()
    pool.join()   #调用join之前,先调用close函数,否则会出错。执行完close后不会有新的进程加入到pool,join函数等待所有子进程结束
    print('休眠测试结束')

运行结果:

进程线程与cpu

1、对于多cpu,多个进程可以并行在多个cpu中计算,当然也会存在进程切换;对于单cpu,多个进程在这个单cpu中是并发运行,根据时间片读取上下文+执行程序+保存上下文。同一个进程同一时间段只能在一个cpu中运行,如果进程数小于cpu数,那么未使用的cpu将会空闲。

2、对于多核cpu,进程中的多线程并行执行。对于单核cpu,多线程在单cpu中并发执行,根据时间片切换线程。同一个线程同一时间段只能在一个cpu内核中运行,如果线程数小于cpu内核数,那么将有多余的内核空闲。

参考:

进程