Python中的进程和线程

来源:互联网 发布:怎么取消淘宝介入 编辑:程序博客网 时间:2024/06/04 05:28

1.多进程

1.1创建进程

Unix/Linux/Mac操作系统都可以使用fork()函数来创建子进程,分别在父进程和子进程内返回,例如

代码:

import os  #  导入os模块print ('当前进程的ID是:%s' % os.getpid()) # os.getpid()返回的是进程的id不是线程ID = os.fork()  # 创建子进程,并返回进程的id,父进程返回的是父进程的id,子进程返回的是0if ID == 0:    print ('这是子进程,ID是:%s。。父进程ID是:%s' % (os.getpid(), os.getppid()))else:    print ('这是父进程,ID是:%s' % os.getpid())
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

结果:

当前进程的ID是:1064这是父进程,ID是:1064这是子进程,ID是:1065。。父进程ID是:1064
  • 1
  • 2
  • 3
  • 4

在Windows中没有fork()调用,可以用multiprocessing模块的Process类,例如

代码:

import osfrom multiprocessing import Processimport os# 子进程要执行的代码def run_proc(name):    print('子线程 %s ,ID是:%s' % (name, os.getpid()))print('当前线程(父线程)的ID是: %s' % os.getpid())p = Process(target=run_proc, args=('test',))  # 创建Process的实例,并传入子线程要执行的函数和参数p.start()  # 子线程开始执行p.join()  # join方法用于线程间的同步,等线程执行完毕后再往下执行print('子线程执行完毕,回到主线程%s' % os.getpid())
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

结果:

当前线程(父线程)的ID是: 1291子线程 test ,ID是:1292子线程执行完毕,回到主线程1291
  • 1
  • 2
  • 3
  • 4

1.2 使用进程池Pool

如果要启动多个子进程,则可用进程池Poll,例如

代码:

from multiprocessing import Poolimport osimport timeimport randomdef child_task(name):    print('子进程 %s ID是:%s 正在运行' % (name, os.getpid()))    start = time.time()    time.sleep(random.random() * 3)  # 随机睡眠一段时间    end = time.time()    print('子进程 %s 运行了 %0.2f 秒' % (name, (end - start)))if __name__ == '__main__':  # 交互模式自动开始执行    print('当前进程(父进程)ID是:%s' % os.getpid())    p = Pool(4)  # 创建进程池实例,大小是4个进程    for i in range(5):  # 循环5次,每次循环都创建一个子进程,大小只有4,则第五个需要等待        p.apply_async(child_task, args=(i,))  # apply_async方法,传入子进程要执行的函数和函数参数(以元组的形式)    print('子进程循环创建完毕,正在等待子进程执行。。')    p.close()  # 关闭进程池,之后就不能添加新的进程了    p.join()  # 如果有进程池,调用join前必须调用close。(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

结果:

子进程循环创建完毕,正在等待子进程执行。。子进程 0 ID是:1776 正在运行子进程 1 ID是:1777 正在运行子进程 2 ID是:1778 正在运行子进程 3 ID是:1779 正在运行子进程 1 运行了 0.24 秒子进程 4 ID是:1777 正在运行子进程 3 运行了 0.64 秒子进程 2 运行了 0.79 秒子进程 0 运行了 1.21 秒子进程 4 运行了 1.56 秒所有进程运行完毕
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

1.2 进程之间的通信

进程与进程之间通过传递对象Queue来通信,例如

代码:

from multiprocessing import Process, Queueimport osimport timeimport randomdef write(q):  # 写数据    for value in ['A', 'B', 'C']:        print('进程 %s 开始写入 %s' % (os.getpid(), value))        q.put(value)        time.sleep(random.random())  # 随机睡眠一段时间,开始写入第二个数据def read(q):  # 读数据    while True:        value = q.get(True)        print('进程 %s 开始读出 %s' % (os.getpid(), value))if __name__ == '__main__':    q = Queue()  # 父进程创建Queue,并传给各个子进程:    pw = Process(target=write, args=(q,))  # 传入进程要执行的函数和函数参数    pr = Process(target=read, args=(q,))    pw.start()  # 启动子进程pw,写入:    pr.start()  # 启动子进程pr,读取:(启动之后,就一直循环着尝试读取,直到被中断)    pw.join()  # 等待pw结束:    pr.terminate()  # pw进程执行结束后,就中断pr,因为pr进程里是死循环,无法等待其结束,只能强行终止:
  • 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

结果:(开启了两个进程,一个读一个写)

进程 1984 开始写入 A进程 1985 开始读出 A进程 1984 开始写入 B进程 1985 开始读出 B进程 1984 开始写入 C进程 1985 开始读出 C
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

2.多线程

2.1 创建多线程

Python 的多线程用到的是threading模块,同启动进程类似,传入要执行的函数,例如.

代码:

import time, threadingdef loop():  # 新线程要执行的函数    print('创建了线程 %s' % threading.current_thread().name)  # current_thread()返回当前线程的名字    for n in range(5):  # 循环五次 , 示例代码        print('线程 %s 循环第 %s 次' % (threading.current_thread().name, n + 1))        time.sleep(1)  # 暂定1秒    print('线程 %s 结束' % threading.current_thread().name)print('最开始线程 %s 正在执行' % threading.current_thread().name)t = threading.Thread(target=loop, name='LoopThread')  # 传入线程要执行的函数和线程的名字,如果不指定名字,系统会有默认的线程名字t.start()t.join()  # 等待线程执行完毕print('线程 %s 结束' % threading.current_thread().name)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

结果:(主线程实例的名字叫MainThread)

最开始线程 MainThread 正在执行创建了线程 LoopThread线程 LoopThread 循环第 1 次线程 LoopThread 循环第 2 次线程 LoopThread 循环第 3 次线程 LoopThread 循环第 4 次线程 LoopThread 循环第 5 次线程 LoopThread 结束线程 MainThread 结束
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

2.1 线程锁Lock

多进程中,各自的进程变量都是各自进程独有的,进程之间互不影响,而线程中,所有线程都可以对同一个变量进行修改,这样就容易造成数据混乱。 
代码:

import threadingimport timebalance = 0  # 银行存款:def change_it(n):    global balance  # 先存后取,结果应该为0 全局变量    balance = balance + n    balance = balance - ndef run_thread(n):    for i in range(100000):         change_it(n)t1 = threading.Thread(target=run_thread, args=(5,))t2 = threading.Thread(target=run_thread, args=(8,))t1.start()t2.start()t1.join()t2.join()print(balance)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24

多次运行后,结果可能就不是0了,因为两个线程交叉执行,造成了数据混乱,可用Lock锁来确定,某个时间只能有一个线程执行该语句,例如

代码:

lock = threading.Lock()...def run_thread(n): # 线程执行函数代码    for i in range(100000):        lock.acquire()  # 先获取锁        try:            change_it(n)        finally:            lock.release()  # 放在finally语句中确保一定会执行,将锁释放
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

线程上锁的好处就是确保某时间段内只有该线程执行,缺点就是无法并发执行线程,效率较低。另可存在多个锁,有可能造成死锁的情况

2.3 python中的多核

Python线程执行前都会有一个GIL锁,然后,每执行100条字节码,解释器就自动释放GIL锁,让别的线程有机会执行,因此多线程在python中,只能用一个核,若要实现多核功能,可用多进程模式

3.Python中的ThreadLocal

上面已经写过,多个线程对全局变量同时做修改时会造成数据混乱,可添加互斥所来控制同一时间只有一个线程访问全局变量,但是很多时候各个线程还有自己的私有变量,如下。

代码:

import threadingdef show(num):    print ('线程 %s 的结果是: %s' % (threading.current_thread().getName(), num))def test_add():    result = 0  # 线程私有变量    for _ in xrange(1000):  # 循环1000次 xrange返回的是生成器,range返回的是list,数据量大的时候用xrange        result += 1    show(result)  # 调用其他函数的时候,需要将私有变量传递过去threads = []for i in range(5):    threads.append(threading.Thread(target=test_add, name=(i + 1)))  # 创建10个线程    threads[i].start()
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18

结果:(各自线程有各自的私有变量)

Thread-1 的结果是: 1000Thread-2 的结果是: 1000Thread-3 的结果是: 1000Thread-4 的结果是: 1000Thread-5 的结果是: 1000
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

若在调用show()函数的时候还要传入其他私有变量,或者还有很多类似show()函数需要调用私有变量,这样每个函数都传递私有变量太繁琐,可在全局定义一个字典,某个线程创建的时候将线程的私有变量(key=线程实例,value=变量值)加入到字典中,这样该线程内部的其他函数调用私有变量的时候不用来回传递也可取到该值,例如

代码:

import threadingdata = {} # 定义的全局字典def show():  # 线程所调用函数不用接收参数,直接获取全局字典的值即可    thread = threading.current_thread()  # 当前线程的实例    print ('线程 %s 的结果是: %s' % (thread.getName(), data[thread]))def test_add():    thread = threading.current_thread()  # 当前线程的实例作为字典的key值    data[thread] = 0  # 初始结果为0    for _ in xrange(1000):        data[thread] += 1    show()  # 此时再调用其他函数,可不用传递参数threads = []for i in range(5):    threads.append(threading.Thread(target=test_add, name=(i + 1)))  # 创建5个线程    threads[i].start()
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22

结果:

线程 1 的结果是: 1000线程 2 的结果是: 1000线程 3 的结果是: 1000线程 4 的结果是: 1000线程 5 的结果是: 1000
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

然而上边的代码并没有做到完全的线程和数据分隔开,而且,每个线程执行的时候都可以访问全局字典,即可以修改其他值,这样可能也会造成数据混乱,因此,可以使用ThreadLocal,其实也就是相当于全局字典,只是封装好了key值,只可以访问和修改相应的value值,例如

代码:

import threadingdata = threading.local()  # 获取ThreadLocal实例def show():    thread = threading.current_thread()  # 当前线程的实例    print ('线程 %s 的结果是: %s' % (thread.getName(), data.num))  # 获取ThreadLocal实例value值data.num(key值已经绑定)def test_add():    data.num = 0  # 直接给THreadLocal实例添加num属性,并赋值为0,还可有其他私有变量data.a,data.b等    for _ in xrange(1000):        data.num += 1    show()threads = []for i in range(5):    threads.append(threading.Thread(target=test_add, name=(i + 1)))  # 创建5个线程    threads[i].start()
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21

结果是一样的

阅读全文
'); })();
0 0
原创粉丝点击
热门IT博客
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 排骨汤里放什么食材好 一洞两枪好痛 筋膜枪哪个牌子好 耳温枪什么牌子好 耳温枪和额温枪哪个好 额温枪和耳温枪哪个好 热熔胶枪哪个牌子好 耳温枪还是额温枪好 耳温枪好还是额温枪好 黄油枪什么牌子质量好 免费送爆枪英雄超好的号满级 水光枪哪个牌子好 水光枪注射好吗 黄油枪什么牌子好 热熔胶枪牌子 耳温枪牌子 星际战甲扫描三个沙漠枪兵 好歌曲 好歌声 好的歌曲 刚刚好歌曲 好歌 班歌选什么好 好歌推荐 好歌推荐100首 怎么能唱好歌 唱好歌的基本方法 经典好歌 好歌大全 怎么可以唱好歌 不为人知的好歌 怎么唱好歌技巧 如何才能唱好歌 天天好歌 好歌多多 好歌金曲 歌曲歌在飞 粤语好歌 咋样才能唱好歌 要怎么唱好歌 有鼻炎能唱好歌吗