python多线程和多进程

Table of Contents

  1. 线程概念
    1. 进程与线程的区别
      1. 线程常用方法
    2. 线程常用方法
      1. 普通创建方式
      2. 重写类,来实现启动线程
      3. 其他方法
    3. GIL
      1. python多线程的工作流程
    4. 线程锁
    5. 互斥锁(mutex)
    6. 递归锁
    7. 信号量(BoundedSemphore)
    8. Event事件
    9. 条件(Condition类)
    10. 定时器
  2. 多进程
    1. 什么是多进程
    2. 相关用法
    3. 进程间的通信
      1. 使用Queue
      2. 使用Pipe
    4. 进程池

多线程

<a id="orgacb2cbe"></a>

线程概念

所谓线程是操作系统能够进行运算调度的最小单元,一个线程是一个执行上下文,是cpu执行命令时的一串指令,一个进程能够并发出多个线程,来执行不同的任务。

<a id="orga1fd5b1"></a>

进程与线程的区别

1.同一进程中的线程共享同一内存空间,但进程之间是独立的。
2.同一进程中的线程共享数据,但是进程之间的数据是独立的。
3.同一进程中的线程可以很方便地通信,但是进程之间通信之间的通信则需要通过代理实现。
4.在一个进程中创建一个线程很容易,但是进程创建一个子进程,则fork一个出来,复制一份内存空间
5.主线程可以操纵同一进程中的其他线程,但是进程只能操纵它的子进程。线程启动快,进程启动慢。线程之间切换,比较耗时,比较占用cpu,比较适合io密集型任务,
而进程之间的切换比较快速,但是进程fork出很多子进程,比较占用内存空间。对主线程的修改可能会对其他线程造成影响,但是进程和进程,进程和子进程,都是一
个独立的内存空间,所以一个进程的修改,不会对其他进程造成影响。

<a id="orgfd4eec7"></a>

线程常用方法

<table border="2" cellspacing="0" cellpadding="6" rules="groups" frame="hsides">

<colgroup>
<col class="org-left" />

<col class="org-left" />
</colgroup>
<thead>
<tr>
<th scope="col" class="org-left">方法</th>
<th scope="col" class="org-left">注释</th>
</tr>
</thead>

<tbody>
<tr>
<td class="org-left">start()</td>
<td class="org-left">线程如果已经准备,启用该方法,则开始启动线程</td>
</tr>
</tbody>

<tbody>
<tr>
<td class="org-left">setName()</td>
<td class="org-left">为线程设置名称</td>
</tr>
</tbody>

<tbody>
<tr>
<td class="org-left">join()</td>
<td class="org-left">逐个执行每个进程,主线程会等到子线程全部执行完毕,在退出</td>
</tr>
</tbody>

<tbody>
<tr>
<td class="org-left">run()</td>
<td class="org-left">可以重写类时,重写run方法,起到自定义线程类的作用</td>
</tr>
</tbody>

<tbody>
<tr>
<td class="org-left">setDaemon(True)</td>
<td class="org-left">设置为守护进程</td>
</tr>
</tbody>
</table>

<a id="orgc9bc3d0"></a>

线程常用方法

<a id="orgd730348"></a>

普通创建方式

import threading
import time
def run(n):
        print("task", n)
        sleep(1)

t1 = threading.Thread(target=run, args=('t1',))
t2 = threading.Thread(target=run, args=('t2',))
t3 = threading.Thread(target=run, args=('t3',))

t1.start()
t2.start()
t3.start()
t1.join()
t2.join()
t3.join()

<a id="orge37d9b1"></a>

重写类,来实现启动线程

class MyThread(threading.Thread):
        def __init__(self, n):
                super().__init__()
                self.n = n

        def run(self, self.n):
                print("task", n)
                time.sleep(1)
                print("1s")
                time.sleep(1)
                print("2s")

if __name__ == "__main__":
        t1 = MyThread("one")
        t2 = MyThread("two")
        t1.start()
        t2.start()

<a id="orgb228d0f"></a>

其他方法

  • threading.currentthread(): 输出当前线程的名称
  • threading.activecount(): 输出当前活跃线程的个数
  • t.setDaemon(True): 设置当前线程为守护进程,只要主进程一退出,子进程也随之退出, 必须在调用start()之前使用,否则会报RuntimeError的错误

<a id="orgb8fbe80"></a>

GIL

1.GIL全称为Global Interpreter Lock(全局解释器锁),全局解释器锁的存在,保证了同一进程中,只能有一个线程正在运行,所以python多线程编程效率并没有那么高,
通过使用多进程的方式,能够有效地提升效率。python2中GIL是通过ticks来计数,计数值到达100,则解释器,就会释放,但是在python3中,GIL是通过计时来实现了,只要时间一到,不管任务完没完成,都会收回
GIL(这种情况下,对cpu密集型运算更有利了一些,但依然问题很大),这种情况下,python对cpu密集型任务并不友好,而对io密集型任务比较友好。
2.GIL在cpython才存在,jpython和pypy中是没用GIL,因为cpython调用的是c语言的原生线程

<a id="org7c0db1d"></a>

python多线程的工作流程

ipython使用多线程的时候,是使用c语言原生线程
1.拿到公共数据
2.申请GIL
3.python解释器调用os原生线程
4.os执行cpu运算
5.时间到了收回GIL,无论是否完成任务
6.如此循环以上过程
7.当其他线程完成后,就继续执行之前的线程(从之前线程执行的上下文处开始执行),每个线程都执行自己的运算,时间到了就切换线程

<a id="org7bb53a5"></a>

线程锁

因为线程之间是公用同一份数据,所以当多个线程对同一数据进行操作时,容易产生脏数据,导致数据出现错误,所以出现了线程锁的概念,在一个线程拥有该锁时,其他线程不能进行操作,保证了,同一数据在同一时刻,
只有一个线程对其进行操作

<a id="orge4ca630"></a>

互斥锁(mutex)

为了防止上述情况的发生,所以有了互斥锁

import threading
# 实例化了一个锁对象
lock = threading.Lock()
def run():
        lock.acquire()  # 获得锁,其他线程不能操作
        print("hello world")
        lock.release()  # 释放锁,其他线程开始竞争

<a id="orga4ec85d"></a>

递归锁

与上面的Lock用法类似,RLock可以嵌套,实现多个锁

import threading
rlock = threading.RLock()
def run():
        rlock.acquire()
        rlock.acquire()
        print("hello world")
        rlock.release()
        print("hello syk")
        rlock.release()

<a id="orgf9efecd"></a>

信号量(BoundedSemphore)

互斥锁只允许一个线程修改数据,信号量则允许多个线程同时修改,就比如厕所有三个坑,只有其中的坑空出来了,才能有人进去

import threading
semphore = threading.BoundedSemphore(5)  # 表示最多有五个线程同时进行操作
def run(i):
        semphore.acquire()
        print("hello",i)

for i in range(20):
        t = threading.Thread(target=run)
        t.start()

while t.active_count != 1:
        pass
else:
        print("all thread has been done")

<a id="org9255bad"></a>

Event事件

python线程的事件主要是用于主线程控制其他线程的执行,event是一个简单的事件同步对象,提供了一下几种方法:

<table border="2" cellspacing="0" cellpadding="6" rules="groups" frame="hsides">

<colgroup>
<col class="org-left" />

<col class="org-left" />
</colgroup>
<thead>
<tr>
<th scope="col" class="org-left">方法</th>
<th scope="col" class="org-left">注释</th>
</tr>
</thead>

<tbody>
<tr>
<td class="org-left">set()</td>
<td class="org-left">将flag置为True</td>
</tr>
</tbody>

<tbody>
<tr>
<td class="org-left">clear()</td>
<td class="org-left">将flag置为False</td>
</tr>
</tbody>

<tbody>
<tr>
<td class="org-left">isset()</td>
<td class="org-left">是否将flag设置为True</td>
</tr>
</tbody>

<tbody>
<tr>
<td class="org-left">wait()</td>
<td class="org-left">一直监听flag,如果没有监听到,则一直为阻塞状态</td>
</tr>
</tbody>
</table>

<a id="org034b100"></a>

条件(Condition类)

使得线程等待,满足条件才将n个线程释放

<a id="org70af908"></a>

定时器

定时器,指定n秒后执行操作

import threading
def hello():
        print("hello world")

t = threading.Timer(1, hello)
t.start()

<a id="org06e9300"></a>

多进程

<a id="org4ad204e"></a>

什么是多进程

1.一个程序的执行实例就是一个进程,进程提供一个程序运行所需要的所有资源(进程本质上是资源的集合)
2.每一个进程都有自己的虚拟地址空间,可执行的代码块,操作系统接口,安全的上下文(记录启动该进程的用户和权限),还有唯一的进程ID,
环境变量,优先级,最大最小的工作空间(内存空间)。

与进程相关的资源:
1.内存页(统一进程下的线程共享同一内存空间)
2.文件描述符
3.安全凭证(启动该进程的用户ID)

<a id="org7331068"></a>

相关用法

from multiprocessing import Process
import time


def hello():
     print("helo world")


if __name__ == "__main__":
     p = Process(target=hello)
     p.start()
     p.join()

<a id="org5b3cf39"></a>

进程间的通信

由于进程间的数据是独立的,所以进程间的通信可以通过Queue和Pipe来实现

<a id="org9d70940"></a>

使用Queue

from  multiprocessing import Process, Queue
def q(p):
        p.put([41, None, 'hello'])

if __name__ == "__main__":
     p = Queue()
     t = Process(target=q, args=(p,))
     t.start()
     print(q.get())
     t.join()

<a id="org179cdd3"></a>

使用Pipe

Pipe的实质是数据的传递而不是数据的共享,在管道的两头都有一个recv和send方法,负责接受和发送,
但是如果两端同时进行相同的操作,那么就会发生错误,破坏管道中的数据。

from multiprocessing import Process, Pipe
import time


def parent_pro(conn):
        conn.send("hello world, my son")
        conn.close()


def child_pro(conn):
        data = conn.recv()
        print(data)
        conn.close()


if __name__ == "__main__":

        parent_pipe, child_pipe = Pipe()
        p1 = Process(target=parent_pro, args=(parent_pipe,))
        p2 = Process(target=child_pro, args=(child_pipe,))
        p1.start()
        p2.start()
        p1.join()
        p2.join()

<a id="org475381f"></a>

进程池

因为多进程比较消耗内存空间,所以在使用多进程时,一般可以使用进程池(用来防止内存被大量消耗), (多线程不需要这种概念,多线程的启动消耗比较小,但是多线程切换比较消耗cpu资源)

常用方法:
1.apply(): 同步执行(串行)
2.applyasync(): 异步执行(并行)
3.terminate(): 立即关闭进程池
4.join(): 主进程等待所有子进程执行完毕,必须在close()和terminate()之后
5.close(): 待所有进程执行完毕之后,关闭进程池

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,793评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,567评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,342评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,825评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,814评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,680评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,033评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,687评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 42,175评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,668评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,775评论 1 332
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,419评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,020评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,978评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,206评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,092评论 2 351
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,510评论 2 343

推荐阅读更多精彩内容

  • 又来到了一个老生常谈的问题,应用层软件开发的程序员要不要了解和深入学习操作系统呢? 今天就这个问题开始,来谈谈操...
    tangsl阅读 4,088评论 0 23
  • 一、进程和线程 进程 进程就是一个执行中的程序实例,每个进程都有自己独立的一块内存空间,一个进程中可以有多个线程。...
    阿敏其人阅读 2,607评论 0 13
  • 有那么一刻我很有联系你的冲动 想假装发错消息给你 我的拳头在手心握出深深的痕迹 我只是太孤独
    珠珠爱吃素阅读 195评论 0 0
  • 这是今天她第三次打她的孩子。 第一次。 孩子想坐摇摇车,摇摇车被几个大一些的孩子围着,她的孩子挤不进去,哭。 她抱...
    我就是那个郭郭啦阅读 421评论 6 4
  • 这是萌面的大猫第三天来到简书,也是从今天开始更新文章和随笔,以后会每天坚持早起,并不一定可以每天更新文章,坚持一年...
    萌面的大猫阅读 133评论 0 0