python进程、线程的学习心得

什么是多线程竞争?

线程不是独立的,同一个进程里的线程,线程间的数据是共享的,多线程操作时,容易造成数据的混乱,线程不安全。

如何解决?

互斥锁。

好处:能够保证某段关键代码执行时,只有一个线程操作,保证原子性,避免多线程下的资源竞争。

坏处:性能下降,阻止了多线程的并发执行。致命问题,有可能产生死锁。

解释一下什么是锁,有哪几种锁?

锁是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()

猜你喜欢

转载自blog.csdn.net/liangkaiping0525/article/details/82591488