什么是多线程竞争?
线程不是独立的,同一个进程里的线程,线程间的数据是共享的,多线程操作时,容易造成数据的混乱,线程不安全。
如何解决?
互斥锁。
好处:能够保证某段关键代码执行时,只有一个线程操作,保证原子性,避免多线程下的资源竞争。
坏处:性能下降,阻止了多线程的并发执行。致命问题,有可能产生死锁。
解释一下什么是锁,有哪几种锁?
锁是python提供的对线程控制的对象。互斥锁,可重入锁,死锁。
互斥锁:同一时刻只允许一个线程操作,具有排他性和唯一性。比如A、B两个线程,A操作时,B只能等着,A执行完,B才能操作。
可重入锁:有时候在同一个线程中,我们可能会多次请求同一资源(就是,获取同一锁钥匙),俗称锁嵌套。
死锁:互相干等着,都不释放锁,程序无法执行下去。
GIL锁(全局解释器锁):限制多线程的同时执行,同一时间,只有一个线程执行,所以cpython里的多线程其实是伪多线程。python使用协程代替多线程来解决,更轻量级的线程,进程和线程的切换时系统确定的,而协程的切换是由程序员确定的,而模块gevent下切换是遇到耗时操作才会切换的。进程有线程,线程有协程。
什么是线程安全,什么是互斥锁?
作用:保证同一时刻只有一个线程访问一个对象的功能。
由于同一进程的多个线程之间是共享系统资源的,多个线程同时对一个对象进行操作时,一个线程对其进行操作尚未结束cpu时间片切换到另一个线程对其操作,再切换回来时,数据已被修改,导致结果出现错误。此时需要对被操作的对象添加互斥锁,保证每个线程对该对象的操作都能得到正确的结果。
说说下面几个概念:同步,异步,阻塞,非阻塞?
同步:排队,一一执行,一个执行完,才执行下一个。
异步:没有先后顺序,同时执行。
阻塞:程序执行到某段,不往下执行了,卡在那里了。
非阻塞:如果这段卡主了,会执行其他代码。
什么是僵尸进程和孤儿进程?怎么避免僵尸进程?
孤儿进程:父进程退出,子进程还在运行的这些子进程都是孤儿进程,孤儿进程将被init 进程(进
程号为1)所收养,并由init 进程对它们完成状态收集工作。
僵尸进程:在UNIX 系统中,一个进程结束了,但是他的父进程没有等待(调用wait / waitpid)他, 那么他将变成一个僵尸进程。
避免僵尸进程的方法:
1.fork 两次用孙子进程去完成子进程的任务;
2.用wait()函数使父进程阻塞;
3.使用信号量,在signal handler 中调用waitpid,这样父进程不用阻塞。
常见问题总结:
1. 线程和进程有什么不同?
```python
进程:1>系统进行资源分配和调度的一个独立单元
2>进程间不共享全局变量,需要进行进程间的通信
3>进程在运行过程中为独立的内存单元
线程:1>进程的一个实体,是CPU调度和分派的基本单位
2>同时对一个全局变量进行修改,容易混乱(不一定执行完就换线程)
3>线程依赖进程的存在,线程并发性高,占用资源比进程少
4>多线程共享非全局变量不用加锁
5>多线程到同一个函数里执行,函数里的变量各是各的
```
2. 什么是单任务、多任务程序?
```python
参考:
单任务是指一次只能运行一个程序,不能同时运行多个程序
多任务是指可以同时运行多个程序
```
3. Linux系统是\_\_\_\_任务\_\_\_\_用户的系统?
```python
Linux系统是多任务多用户的系统
```
4. 以单核cpu为例,它是怎样完成多任务的?
```python
轮流切换执行
微观上,在任何一个时刻只有一个程序被执行
但切换速度非常的快,因此在宏观上,看上去多个任务在一起执行一样
```
5. 怎样区分并行和并发?
```python
简单来讲:
并行是指多个cpu同时执行多个任务
并发是指单个cpu轮流切换执行多个任务
```
6. 程序和进程有什么区别?
```python
简而言之,代码没有被运行之前是一个程序,当运行起来后就能成为进程
```
8. 子进程和父进程是什么?
```python
通过进程a产生的进程b,a进程就是父进程,b就是子进程
```
9. getpid、getppid的作用是什么?
```python
getpid 获取当前进程的进程号
getppid 获取当前进程的父进程的进程号
```
10. 创建出来的多个子进程,同时对一个相同名字的全局变量操作时会出错么?为什么?
```python
不会出错
因为进程之间的资源是不共享的,各自拥有各自的一份该变量,操作互不影响
```
12. 创建出来的子进程和父进程到底是谁先执行?为什么?
```python
不确定
因为多任务中,谁先被执行,是由cpu的调度算法来决定的,cpu会保证每个进程都能被平均的执行一段时间,一次你看上去会是随机的
```
14. multiprocessing模块的目的是什么?
```python
使用multiprocessing模块中的Process创建一个子进程
```
15. 怎样用multiprocessing模块中的Process创建一个子进程?请写出基本代码
from multiprocessing import Process
def mission():
for i in range(5):
print(i)
if __name__ == "__main__":
p = Process(target=mission) # 创建进程实例
p.start() # 启动子进程
p.join() # 让父进程等待
16. multiprocessing模块中的Process创建了一个子进程后,怎样让子进程开始执行?
```python
调用start方法
```
18. 如果一个程序需要同时执行多个任务,那么一般会怎么做?
```python
使用多进程或者多线程来实现
```
20. 什么是进程池?有什么用?
```python
进程池就是创建出一定固定数量的进程,去执行多个任务
节约创建进程和销毁进程所消耗的资源和空间
当某个任务被执行完毕后,利用该进程再去执行其他的任务,大大提高效率
```
19. 为了完成多个任务一起执行,可以创建多个子进程来执行任务,那么为什么还要进程池呢?
```python
因为每创建一个进程都会申请内存空间,消耗资源,进程结束又要回收资源
如果反复创建进程,又结束进程,会严重影响性能
进程池的目的就是复用进程,大大提高程序的运行效率
```
21. 什么是进程间通信?
```python
简而言之
进程间的资源是不共享的,因此如果在不同进程间的任务需要相互使用对方的资源或信息
那么就需要在进程之间传递信息、传递资源,这就是进程间通信
```
22. 为什么需要进程间通信?
```python
同上
```
23. multiprocessing模块中Queue怎样发送、取出数据?
```python
q = Queue()
q.put(数据) # 存放数据
q.get() # 取出数据
```
# 关卡二
练习题:1. 使用Process创建1个子进程,让子进程每1秒钟打印1个数字,数字从1开始一直到10,即1.2.3......10
# coding=utf-8
import time
from multiprocessing import Process
# 定义子进程的需要执行的任务 函数
def mission():
#打印1-10
for i in range(1,11):
print(i)
time.sleep(1)
def main():
p = Process(target=mission)
p.start()
p.join()
if __name__ == "__main__":
main()
2. 使用multiprocessing模块中的Queue,完成子进程中将hello传递到父进程中,父进程打印出来
# coding=utf-8
import time
from multiprocessing import Process, Queue
# 定义父进程的需要执行的任务 函数
def parent_mission(que):
data = que.get() # 从队列获取数据打印
print(data)
time.sleep(5)
# 定义子进程的需要执行的任务 函数
def children_mission(que):
data = "hello" # 输入hello就会被父进程拿到
que.put(data) # 往队列添加数据
time.sleep(3)
def main():
q = Queue() # 在父进程中定义队列,实现与子进程通信
p = Process(target=children_mission, args=(q,))
p.start() # 启动子进程 执行任务
parent_mission(q) # 在父进程中 执行任务
p.join()
if __name__ == "__main__":
main()
1. 使用进程池完成如下要求:
* 将/usr/lib/python3.5文件夹下的所有py结尾的文件copy到 桌面上的Test文件夹中
* 用多任务(多进程或者多线程)的方式完成Test文件夹中的所有内容复制
* 新的文件夹的名字为“Test-附件”
* 在复制文件的过程中,实时显示复制的进度
import multiprocessing
import os
import time
import random
def copy_file(queue, file_name,source_folder_name, dest_folder_name):
"""copy文件到指定的路径"""
f_read = open(source_folder_name + "/" + file_name, "rb")
f_write = open(dest_folder_name + "/" + file_name, "wb")
while True:
time.sleep(random.random())
content = f_read.read(1024)
if content:
f_write.write(content)
else:
break
f_read.close()
f_write.close()
# 发送已经拷贝完毕的文件名字
queue.put(file_name)
def main():
# 获取要复制的文件夹
source_folder_name = input("请输入要复制文件夹名字:")
# 整理目标文件夹
dest_folder_name = source_folder_name + "[副本]"
# 创建目标文件夹
try:
os.mkdir(dest_folder_name)
except:
pass # 如果文件夹已经存在,那么创建会失败
# 获取这个文件夹中所有的普通文件名
file_names = os.listdir(source_folder_name)
# 创建Queue
queue = multiprocessing.Manager().Queue()
# 创建进程池
pool = multiprocessing.Pool(3)
for file_name in file_names:
# 向进程池中添加任务
pool.apply_async(copy_file, args=(queue, file_name, source_folder_name, dest_folder_name))
# 主进程显示进度
pool.close()
all_file_num = len(file_names)
while True:
file_name = queue.get()
if file_name in file_names:
file_names.remove(file_name)
copy_rate = (all_file_num-len(file_names))*100/all_file_num
print("\r%.2f...(%s)" % (copy_rate, file_name) + " "*50, end="")
if copy_rate >= 100:
break
print()
if __name__ == "__main__":
main()