线程池和进程池

线程池和进程池

线程安全

​ 单线程程序不会有线程安全的问题,但多线程程序就可能出现线程安全的问题,举个例子,当多个线程同时访问同一个数据时并对其做出操作时,可能会出错,原因是线程一访问某个数据并修改了它,但还没来得及保存,线程二也访问了这个数据,但取到的数据还是未修改的,所以线程二取到的是一个脏数据(错误数据)。

线程安全解决方案——加锁

1.Lock锁

from threading import Thread,Lock#导入线程模块和Lock锁模块
account = 10000#定义一个全局变量,表示账户余额
account_lock = Lock()#创建锁对象,一个数据对应一个锁对象
#定义一个存钱的函数
def save_money(num):
    print(f'开始存钱')
    global account
    account_lock.acquire()#加锁
    account += num
    print(f'存钱成功,当前余额:{account}')
    account_lock.release()#解锁
#定义一个取钱函数
def draw_money(num):
    print(f'开始存钱')
    global account
    account_lock.acquire()#加锁
    if account < num:
        return
    account -= num
    print(f'存钱成功,当前余额:{account}')
    account_lock.release()#解锁
#创建子线程对象
t1 = Thread(target=save_money,args=(2000,))
t2 = Thread(target=draw_money,args=(5000,))
#启动子线程
t1.start()
t2.start()

运行结果

H:\Python虚拟环境\Spyder\Scripts\python.exe H:/Day8ThreadAndProcess/02-Lock锁.py
开始取钱
开始存钱
取钱成功,当前余额:5000
存钱成功,当前余额:7000

Process finished with exit code 0

2.RLock锁

from threading import Thread,RLock
import time
account = 10000
account_lock = RLock()
def save_money(num):
    #RLock锁只需要加锁,with作用域的代码执行完后会自动解锁
    with account_lock:
        print('开始存钱')
        global account
        balance = account
        time.sleep(2)
        account = num + balance
        print(f'存钱成功,当前余额:{account}')
def draw_money(num):
    with account_lock:
        print('开始取钱')
        global account
        balance = account
        if balance >= num:
            account = balance - num
            time.sleep(2)
            print(f'取钱成功,当前余额:{account}')
        else:
            print('余额不足')
t1 = Thread(target=save_money,args=(2000,))
t2 = Thread(target=draw_money,args=(5000,))
t2.start()
t1.start()

运行结果同上

队列

​ 队列是一个容器,而且和迭代器比较像,只能一个一个往外去数据,和迭代器不同是队列除了有将数据放进去的进口,还有将数据取出的出口,相当于一个管道,也就是说队列的数据是先进先出,而迭代器里的数据是先进后出,队列里的数据无法遍历,队列还有一个特点,就是在用死循环取队列的数据时不会报错,因为在取完队列数据后队列会进入等待状态,等到有数据放进队列就继续取,如下:

from queue import Queue
q = Queue#创建队列对象
for i in range(10):
    q.put(i)#放数据
while True:
    num = q.get()#取数据
    print(num)
 

运行结果:代码不会报错,但也不会结束,队列在死循环中会一直等

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-JopjABdi-1604494280484)(C:\Users\dell\AppData\Roaming\Typora\typora-user-images\image-20201104200844753.png)]

线程池

线程池可以理解为将多个子线程统一管理工具

实例代码

from threadpool import ThreadPool,makeRequests
import time
#定义一个函数
def func(a):
    time.sleep(1)
    print(a)
#创建线程池对象
t_pool = ThreadPool(10)#线程池有10个子线程
#创建任务列表/makeRequests(任务对应的函数,函数的参数列表)
task_lst = makeRequests(func,[x for x in range(10)])
#在线程池中添加任务
for task in task_lst:
    t_pool.putRequest(task)
start_time = time.time()
t_pool.wait()#执行和等待
end_time = time.time()
print(f'执行时间:{end_time-start_time}s')

运行结果:其执行时间是单线程执行时间的1/10

H:\Python虚拟环境\Spyder\Scripts\python.exe H:/Day8ThreadAndProcess/test.py
6
4
3
2
1
0
5
8
9
7
执行时间:1.0014607906341553s

Process finished with exit code 0

进程池

​ 进程池的概念和线程池一样,进程池就是多个子进程的管理工具,不同之处在于线程池里的每个线程可以访问并操作同一个数据,而进程池不行,进程池在创建时,会在每个子进程里拷贝一份主进程的数据,子进程只能操作拷贝的数据,无法操作主进程的数据

from multiprocessing import Pool
import time
def func(a):
    time.sleep(1)
    print(a)
if __name__=='__main__':
    #创建进程池对象
    p_pool = Pool(3)#3个子进程
    start_time = time.time()
    #添加任务
    p_pool.map(func,[x for x in range(9)])#添加多个任务
    #p_pool.apply(func,(10,))#添加一个任务
    #p_pool.apply_async(func,(11,))#添加一个任务,不过是和主进程异步执行,前两个都是和主进程同步执行
    """线程池添加任务后会自动启动"""
    p_pool.close()#关闭线程池
    p_pool.join()#等待线程池的任务执行完成
    end_time = time.time()
    print(f'执行时间:{end_time-start_time}s')

运行结果:执行时间是单进程执行时间的1/3

H:\Python虚拟环境\Spyder\Scripts\python.exe H:/Day8ThreadAndProcess/test.py
0
1
2
3
4
5
6
7
8
执行时间:3.5766236782073975s

Process finished with exit code 0

执行时间是单进程执行时间的1/3

H:\Python虚拟环境\Spyder\Scripts\python.exe H:/Day8ThreadAndProcess/test.py
0
1
2
3
4
5
6
7
8
执行时间:3.5766236782073975s

Process finished with exit code 0

线程池进程都是一种资源,用于管理和复用多个线程或进程,以提高程序的性能和效率。 线程池线程池是一组预先创建好的线程,它们可以被重复使用来处理多个任务。线程池可以减少线程创建和销毁的开销,同时也可以控制线程的数量,避免线程过多导致的性能问题。线程池一般包括一个任务队列和多个工作线程,任务队列用于存放需要执行的任务,而工作线程则用于执行任务。当有任务需要执行时,线程池会从任务队列中获取一个任务分配给一个空闲的工作线程来执行。 进程进程是一组预先创建好的进程,它们可以被重复使用来处理多个任务。进程可以减少进程创建和销毁的开销,同时也可以控制进程的数量,避免进程过多导致的性能问题。进程一般包括一个任务队列和多个工作进程,任务队列用于存放需要执行的任务,而工作进程则用于执行任务。当有任务需要执行时,进程会从任务队列中获取一个任务分配给一个空闲的工作进程来执行。 下面是一个简单的使用C语言实现的线程池示例代码: ```c #include <pthread.h> #include <stdio.h> #include <stdlib.h> #include <unistd.h> #define MAX_THREADS 10 #define MAX_TASKS 20 typedef struct { void (*task)(void *); void *arg; } task_t; typedef struct { int count; int head; int tail; pthread_mutex_t lock; pthread_cond_t notify; task_t tasks[MAX_TASKS]; } task_queue_t; typedef struct { pthread_t thread; task_queue_t *queue; } worker_t; typedef struct { int count; worker_t workers[MAX_THREADS]; } thread_pool_t; void task_queue_init(task_queue_t *queue) { queue->count = 0; queue->head = 0; queue->tail = 0; pthread_mutex_init(&queue->lock, NULL); pthread_cond_init(&queue->notify, NULL); } void task_queue_push(task_queue_t *queue, task_t task) { pthread_mutex_lock(&queue->lock); while (queue->count == MAX_TASKS) { pthread_cond_wait(&queue->notify, &queue->lock); } queue->tasks[queue->tail] = task; queue->tail = (queue->tail + 1) % MAX_TASKS; queue->count++; pthread_cond_signal(&queue->notify); pthread_mutex_unlock(&queue->lock); } task_t task_queue_pop(task_queue_t *queue) { pthread_mutex_lock(&queue->lock); while (queue->count == 0) { pthread_cond_wait(&queue->notify, &queue->lock); } task_t task = queue->tasks[queue->head]; queue->head = (queue->head + 1) % MAX_TASKS; queue->count--; pthread_cond_signal(&queue->notify); pthread_mutex_unlock(&queue->lock); return task; } void *worker_thread(void *arg) { worker_t *worker = (worker_t *)arg; task_queue_t *queue = worker->queue; while (1) { task_t task = task_queue_pop(queue); task.task(task.arg); } return NULL; } void thread_pool_init(thread_pool_t *pool) { pool->count = 0; } void thread_pool_add_worker(thread_pool_t *pool, task_queue_t *queue) { worker_t *worker = &pool->workers[pool->count++]; worker->queue = queue; pthread_create(&worker->thread, NULL, worker_thread, worker); } void thread_pool_submit_task(thread_pool_t *pool, task_t task) { task_queue_push(&pool->workers[0].queue, task); } void print_task(void *arg) { int *value = (int *)arg; printf("Task: %d\n", *value); sleep(1); } int main() { thread_pool_t pool; task_queue_t queue; task_queue_init(&queue); thread_pool_init(&pool); thread_pool_add_worker(&pool, &queue); int i; for (i = 0; i < 20; i++) { int *value = malloc(sizeof(int)); *value = i; task_t task = {print_task, value}; thread_pool_submit_task(&pool, task); } pthread_exit(NULL); return 0; } ``` 这个例子中,我们使用了一个任务队列来存放需要执行的任务,然后使用线程池来管理多个工作线程,执行任务。在主函数中,我们创建了20个任务,然后将它们提交给线程池来执行。在任务函数中,我们简单地打印出任务的编号并休眠一秒钟,模拟任务的执行过程。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值