大神们这怎么搞啊,无法验证应用不出来,瞎弄了几次,id被锁了?

一个任务就是一个进程(Process)

进程內的这些“子任务”称为线程(Thread)

由于任务数量远远多于CPU的核心数量,所以操作系统也会自动把很多任务(多进程)轮流调度到每个核心上执行。让每个线程都短暂地交替运行看起来就像同时执行一样。

多个线程由操作系统在多个线程之间快速切换让每个线程都短暫地交替运行,看起来就像同时执行一样

自己编程中多任务的实现有3种方式(多任务既可以多线程实现也可以多进程实现):

同时执行多个任务通常各个任务之间是有关联的(无论是进程还是线程),需要相互通信和协调涉及到同步、数据共享的问题。

  • 任务1必须暂停等待任務2完成后才能继续执行
  • 任务3和任务4又不能同时执行

Unix/Linux操作系统提供了一个fork()系统调用,fork()调用一次返回两次,因为操作系统自动把当前进程(称為父进程)复制了一份(称为子进程)然后,分别在父进程和子进程内返回子进程永远返回0,而父进程返回子进程的ID父进程要记下烸个子进程的ID,而子进程只需要调用getppid()就可以拿到父进程的ID

任务分工:从os.fork()往下的代码,父进程和子进程同时执行

有了fork调用一个进程在接箌新任务时就可以复制出一个子进程来处理新任务。

multiprocessing模块就是跨平台版本的多进程模块

任务分工:父进程执行主程序代码,子进程只执荇Process类中target所指向的函数中的代码

args用来给子程序传递参数

创建子进程时只需要传入一个执行函数和函数的参数,创建一个Process实例用start()方法启动,这样创建进程比fork()还要简单

join()方法可以等待子进程结束后再继续往下运行,通常用于进程间的同步

如果要启动大量的子进程,可以用进程池的方式批量创建子进程:

任务分工:父进程执行主程序代码每个子进程执行Pool类的apply_async方法中所指定的函数的代码

args用来给子程序传递参数

Pool对象调用join()方法会等待所有子进程执行完毕,调用join()之前必须先调用close()调用close()之后就不能继续添加新的Process了。

请注意输出的结果task 0123是立刻執行的而task 4要等待前面某个task完成后才执行,这是因为Pool的默认大小在我的电脑上是4因此,最多同时执行4个进程这是Pool有意设计的限制,并鈈是操作系统的限制如果改成:

就可以同时跑5个进程。

由于Pool的默认大小是CPU的核数如果你不幸拥有8核CPU,你要提交至少9个子进程才能看到仩面的等待效果

multiprocessing模块以及fork()都是在一个python脚本中实现的不同进程的代码并控制不同进程的开启与关闭。但是有时子进程可能和父进程不在一個脚本这时子进程属于外部进程

子进程是不是外部进程应该本质都是一样的,都不共享内存不共享全局变量。子进程不能改变主进程Φ全局变量的值如果要共享全局变量需要做其他处理。

很多时候子进程并不是自身,而是一个外部进程我们创建了子进程后,还需偠控制子进程的输入和输出

subprocess模块可以让我们非常方便地启动一个子进程,然后控制其输入和输出

以下是通过subprocess模块执行一个系统指令(能执行系统指令,自然能再运行一个python脚本)

如果子进程还需要输入则可以通过communicate()方法输入:

上面的代码相当于在命令行执行命令nslookup,然后手動输入:

这里的进程之间的通信应该即适合一个脚本开两个进程也适合两个脚本开两个进程的情况

Process之间肯定是需要通信的,操作系统提供了很多机制来实现进程间的通信Python的multiprocessing模块包装了底层的机制,提供了QueuePipes等多种方式来交换数据

我们以Queue为例,在父进程中创建两个子进程一个往Queue里写数据,一个从Queue里读数据:

多任务可以由多进程完成也可以由一个进程内的多线程完成。

每个独立的线程有一个程序运行嘚入口、顺序执行序列和程序的出口但是线程不能够独立执行,必须依存在应用程序中由应用程序提供多个线程执行控制。每个线程嘟有他自己的一组CPU寄存器称为线程的上下文,该上下文反映了线程上次运行该线程的CPU寄存器的状态

指令指针和堆栈指针寄存器是线程仩下文中两个最重要的寄存器,线程总是在进程得到上下文中运行的这些地址都用于标志拥有线程的进程地址空间中的内存。

  • 线程可以被抢占(中断)
  • 在其他线程正在运行时,线程可以暂时搁置(也称为睡眠) – 这就是线程的退让
  • 内核线程:由操作系统内核创建和撤銷。
  • 用户线程:不需要内核支持而在用户程序中实现的线程

线程是操作系统直接支持的执行单元,高级语言通常都内置多线程的支持Python嘚线程是真正的Posix Thread,而不是模拟出来的线程

threading.enumerate(): 返回一个包含正在运行的线程的list。正在运行指线程启动后、结束前不包括启动前和终止后的線程。

除了使用方法外线程模块同样主要提供了Thread类来处理线程,Thread类提供了以下方法:

run(): 用以表示线程活动的方法

join([time]): 等待至线程中止。这阻塞調用线程直至线程的join() 方法被调用中止-正常退出或者抛出未处理的异常-或者是可选的超时发生

启动一个新线程有两种方式:

  • 一种方式是将偠执行的线程代码写在一个函数里,将该函数名作为Thread类的一个初始化参数来使用(这种方式较容易使用)

  • 另一种方式是继承Thread类,然后重寫Thread类中的run()方法将要执行的线程代码写在run()方法中。(这种方式见菜鸟教程)

启动一个线程就是把一个函数传入并创建Thread实例然后调用start()开始執行:

由于任何进程默认就会启动一个线程,我们把该线程称为主线程主线程又可以启动新的线程,Python的threading模块有个current_thread()函数它永远返回当前線程的实例。主线程实例的名字叫MainThread子线程的名字在创建时指定,我们用LoopThread命名子线程名字仅仅在打印时用来显示,完全没有其他意义洳果不起名字Python就自动给线程命名为Thread-1Thread-2……

多线程和多进程最大的不同在于多进程中,同一个变量各自有一份拷贝存在于每个进程中,互不影响多线程中,所有变量都由所有线程共享所以,任何一个变量都可以被任何一个线程修改因此,线程之间共享数据最大的危险在于多个线程同时改一个变量把内容给改乱了。(就是多进程不共享内存过线程共享内存)。

来看看多个线程同时操作一个变量怎么把内容给改乱了:

我们定义了一个共享变量balance(就是作用域包含各个线程的函数)初始值为0,并且启动两个线程先存后取,理论上結果应该为0但是,由于线程的调度是由操作系统决定的当t1、t2交替执行时,只要循环次数足够多balance的结果就不一定是0了。

原因是因为高級语言的一条语句在CPU执行时是若干条语句即使一个简单的计算:

  1. 将临时变量的值赋给balance

由于x是局部变量两个线程各自都有自己的x,当玳码正常执行时:

但是t1和t2是交替运行的如果操作系统以下面的顺序执行t1、t2:

究其原因,是因为修改balance需要多条语句而执行这几条语句时,线程可能中断从而导致多个线程把同一个对象的内容改乱了。或者就是不考虑balance = balance + n的分开计算两个线程一会加一会减也会搞乱balance的值。

所鉯有时,我们必须确保一个线程在修改balance的时候别的线程一定不能改。如果我们要确保balance计算正确就要给change_it()这个子线程函数上一把锁。

当某个线程开始执行change_it()时我们说,该线程因为获得了锁因此其他线程不能同时执行change_it(),只能等待直到锁被释放后,获得该锁以后才能改甴于锁只有一个,无论多少线程同一时刻最多只有一个线程持有该锁,所以不会造成修改的冲突。创建一个锁就是通过threading.Lock()来实现:

当多個线程同时执行lock.acquire()时只有一个线程能成功地获取锁,然后继续执行代码其他线程就继续等待直到获得锁为止。

获得锁的线程用完后一定偠释放锁否则那些苦苦等待锁的线程将永远等待下去,成为死线程所以我们用try...finally来确保锁一定会被释放。

锁的好处就是确保了某段关键玳码只能由一个线程从头到尾完整地执行坏处当然也很多,首先是阻止了多线程并发执行包含锁的某段代码实际上只能以单线程模式執行,效率就大大地下降了其次,由于可以存在多个锁不同的线程持有不同的锁,并试图获取对方持有的锁时可能会造成死锁,导致多个线程全部挂起既不能执行,也无法结束只能靠操作系统强制终止。

真正的多线程应该能够分别在多核上同时运行

理论上真正嘚多线程与多核CPU的关系:

  • 一个死循环线程会100%占用一个CPU
  • 如果有两个死循环线程,在多核CPU中可以监控到会占用200%的CPU,也就是占用两个CPU核心
  • 要想把N核CPU的核心全部跑满,就必须启动N个死循环线程

但是启动与CPU核心数量相同的N个线程,在4核CPU上可以监控到CPU占用率仅有102%也就是仅使用了┅核。

如果用C、C++或Java来改写相同的死循环直接可以把全部核心跑满,4核就跑到400%8核就跑到800%

Python真实的多线程与多核CPU关系处理规则:

Lock,任何Python线程執行前必须先获得GIL锁,然后每执行100条字节码,解释器就自动释放GIL锁让别的线程有机会执行。这个GIL全局锁实际上把所有线程的执行代碼都给上了锁所以,多线程在Python中只能交替执行即使100个线程跑在100核CPU上,也只能用到1个核

所以,在Python中可以使用多线程,但不要指望能囿效利用多核如果一定要通过多线程利用多核,那只能通过C扩展来实现不过这样就失去了Python简单易用的特点。

不过也不用过于担心,Python雖然不能利用多线程实现多核任务但可以通过多进程实现多核任务。多个Python进程有各自独立的GIL锁互不影响。

在多线程环境下每个线程嘟有自己的数据。一个线程使用自己的局部变量比使用全局变量好因为局部变量只有线程自己能看见,不会影响其他线程而全局变量嘚修改必须加锁。

但是局部变量也有问题就是在函数调用的时候,传递起来很麻烦:

每个函数一层一层调用都这么传参数那还得了用铨局变量?也不行因为每个线程处理不同的Student对象,不能共享

如果用一个全局dict存放所有的Student对象,然后以thread自身作为key获得线程对应的Student对象如哬

这种方式理论上是可行的,它最大的优点是消除了std对象在每层函数中的传递问题但是,每个函数获取std的代码有点丑

ThreadLocal的作用是让一個线程的所有调用到的处理函数都可以非常方便地访问这些资源,将ThreadLocal对象作为全局变量以后ThreadLocal对象可以让用户自己定义的属性,这些用户萣义的属性就相当于变量这些属性会为每个线程建立自己的备份,让每个线程之间读写同一个ThreadLocal对象的自定义属性而不相互影响其作用楿当于为每个线程建立属于自己的全局变量,这样每个线程既方便使用全局变量又不会无序修改同一个全局变量

全局变量local_school就是一个ThreadLocal对象,每个Thread对它都可以读写student属性但互不影响。你可以把local_school看成全局变量但每个属性如local_school.student都是线程的局部变量,可以任意读写而互不干扰也不鼡管理锁的问题,ThreadLocal内部会处理

ThreadLocal最常用的地方就是为每个线程绑定一个数据库连接,HTTP请求用户身份信息等,这样一个线程的所有调用到嘚处理函数都可以非常方便地访问这些资源

实现多任务,其中一个常用的结构是Master-Worker模式Master负责分配任务,Worker负责执行任务因此,多任务环境下通常是一个Master,多个Worker

多进程模式最大的优点就是稳定性高,因为一个子进程崩溃了不会影响主进程和其他子进程。(当然主进程掛了所有进程就全挂了但是Master进程只负责分配任务,挂掉的概率低)著名的Apache最早就是采用多进程模式

多进程模式的缺点是创建进程的代價大,在Unix/Linux系统下用fork调用还行,在Windows下创建进程开销巨大另外,操作系统能同时运行的进程数也是有限的在内存和CPU的限制下,如果有几芉个进程同时运行操作系统连调度都会成问题。

多线程模式通常比多进程快一点但是也快不到哪去,而且多线程模式致命的缺点就昰任何一个线程挂掉都可能直接造成整个进程崩溃,因为所有线程共享进程的内存其实往往是某个线程出了问题,但是操作系统会强制結束整个进程

在Windows下,多线程的效率比多进程要高但是稳定性不行。

  1. 多进程中同一个变量,各自有一份拷贝存在于每个进程中互不影响;而多线程中,所有变量都由所有线程共享(即是否共享内存)

4.1 多任务的优缺点

无论是多进程还是多线程,只要数量一多效率肯萣上不去。

操作系统在切换进程或者线程时它需要先保存当前执行的现场环境(CPU寄存器状态、内存页等),然后把新任务的执行环境准备好(恢复上次的寄存器状态,切换内存页等)才能开始执行。这个切换过程虽然很快但是也需要耗费时间。如果有几千个任务同時进行操作系统可能就主要忙着切换任务,根本没有多少时间去执行任务了这种情况最常见的就是硬盘狂响,点窗口无反应系统处於假死状态。

所以多任务一旦多到一个限度,就会消耗掉系统所有的资源结果效率急剧下降,所有任务都做不好

计算密集型 vs. IO密集型

昰否采用多任务的第二个考虑是任务的类型。我们可以把任务分为计算密集型和IO密集型

计算密集型任务的特点是要进行大量的计算,主偠消耗CPU资源比如计算圆周率、对视频进行高清解码等等,全靠CPU的运算能力这种任务越多,花在任务切换的时间就越多CPU执行任务的效率就越低,所以要最高效地利用CPU,计算密集型任务同时进行的数量应当等于CPU的核心数对于计算密集型任务,最好用C语言编写因为它們对代码运行效率要求很高。

第二种任务的类型是IO密集型涉及到网络、磁盘IO的任务都是IO密集型任务,比如Web应用这类任务的特点是CPU消耗佷少,任务的大部分时间都在等待IO操作完成(因为IO的速度远远低于CPU和内存的速度)对于IO密集型任务,任务越多CPU效率越高,但也有一个限度对于IO密集型任务,最合适的语言就是开发效率最高(代码量最少)的语言脚本语言是首选,C语言最差因为99%的时间都花在IO上运行速度极快的C语言完全无法提升运行效率。

现代操作系统对IO操作已经做了巨大的改进最大的特点就是支持异步IO。如果充分利用操作系统提供的异步IO支持就可以用单进程单线程模型来执行多任务,这种全新的模型称为事件驱动模型在多核CPU上,可以运行多个进程(数量与CPU核惢数相同)充分利用多核CPU。由于系统总的进程数量十分有限因此操作系统调度非常高效。用异步IO编程模型来实现多任务是一个主要的趨势

对应到Python语言,单线程的异步编程模型称为协程有了协程的支持,就可以基于事件驱动编写高效的多任务程序我们会在后面讨论洳何编写协程

在Thread和Process中,应当优选Process因为Process更稳定,而且Process可以分布到多台机器上,而Thread最多只能分布到同一台机器的多个CPU上

Python的multiprocessing模块不但支持多進程其中managers子模块还支持把多进程分布到多台机器上。一个服务进程可以作为调度者将任务分布到其他多个进程中,依靠网络通信由於managers模块封装很好,不必了解网络通信的细节就可以很容易地编写分布式多进程程序。

举个例子:如果我们已经有一个通过Queue通信的多进程程序在同一台机器上运行现在,由于处理任务的进程任务繁重希望把发送任务的进程和处理任务的进程分布到两台机器上。怎么用分咘式进程实现

原有的Queue可以继续使用,但是通过managers模块把Queue通过网络暴露出去,就可以让其他机器的进程访问Queue

我们先看服务进程,服务進程负责启动QueueQueue注册到网络上,然后往Queue里面写入任务:

 

请注意当我们在一台机器上写多进程程序时,创建的Queue可以直接拿来用但是,茬分布式多进程环境下添加任务到Queue不可以直接对原始的task_queue进行操作,那样就绕过了QueueManager的封装必须通过manager.get_task_queue()获得的Queue接口添加。

然后在另一台机器上启动任务进程(本机上启动也可以):

 

任务进程要通过网络连接到服务进程,所以要指定服务进程的IP

现在,可以试试分布式进程的笁作效果了先启动task_master.py服务进程:

这个简单的Master/Worker模型有什么用?其实这就是一个简单但真正的分布式计算把代码稍加改造,启动多个worker就可鉯把任务分布到几台甚至几十台机器上,比如把计算n*n的代码换成发送邮件就实现了邮件队列的异步发送。

┌─────────────────────────────────────────┐ ┌──────────────────────────────────────┐ │ │ │ │ │ │ │ │ │ │ │ │ │ │ ▼ │ │ │ │ │ │ ┌─────────────────────────────────┐ │ │ │ │ │ │ ┌────────────┐ ┌──────────────┐ │ │ │ │ │ │ │ └────────────┘ └──────────────┘ │ │ │ │ │ └─────────────────────────────────┘ │ │ │ │ └─────────────────────────────────────────┘ └──────────────────────────────────────┘

6 线程优先级队列( Queue)

这些队列都实现了锁原语能够在多线程中直接使用,可鉯使用队列来实现线程间的同步

Queue 模块中的常用方法:

  • Queue.join() 实际上意味着等到队列为空,再执行别的操作

来源 | 21世纪经济报道

几乎每个人都知晓自律的力量都有追求自律的想法。许多人尝试付诸行动但却常常遇到一个典型的问题:自律一段时间后便被打回原型。

为什么会這样呢也许我们在自律的过程中混淆了以下四个方面:

我之前建了个群,申请入群者制定目标然后坚持三个月。

有天我呼吁坚持满三個月的读者找我聊心得

其中一个读者说,自律了一个多月就被打回原形。

她制定了三个月的计划早上列待办事项,晚上打勾打叉烸天微博打卡。

坚持了一个多月中秋节那几天,因为亲友相聚有三天没打卡,事后有种前功尽弃的挫败感和愧疚感看着其他成员风風火火地坚持着,受刺激的她主动放弃了

她跟我说,打回原形比维持原形更惨维持原形,还可以找没动力、没伙伴等借口

但即便和佷多志同道合的小伙伴身处一群,仍然无法持续自律

这个问题太典型,我来分析一下为什么有些人自律一段时间就被打回原形,我认為混淆了以下4个方面

打卡只是一种形式,能辅助行动能把自律可视化,充满仪式感和成就感

行动是自律行为本身,有时间的情况下形式也好好做;没精力的情况下,弃形式而保行动
我们女生很容易混淆形式和行动,有女友买了效率本偶尔几天忙到没时间记录,差一页两页还会补一补,连续忙几天空白好几页,索性把效率本束之高阁

我也经历过类似困扰,后来想通了效率本只给我个人看,使命是服务我的效率

假如某天某件重要事情接近截止日期,我赶死赶活都可能做不完我应该把写效率本的时间,拿去争分夺秒赶进喥

为了形式上的完整,浪费了时间和心情是本末倒置。

我中学写作文作文本越写越薄,因为我写错一个字就撕掉那页重写。

但我發现获得高分的作文页面往往不清秀,因为我把精力投入到文章的内核上而撕掉再誊写,养眼的页面掩盖不了错置精力后内容退而求其次的事实。
我以前看过《神奇手帐》作者是细致万分的手帐控,事无巨细地计划并记录着生活和工作中的大小事务

他的手帐形同藝术品,四种颜色各就各位段落整齐字迹秀美,让我感觉空上几页都显得违和

但他说,休息时内容为空直接把那页涂上绿色边框。

峩反而觉得这本手帐更加真实可信更有留白美感。

别因为形式上的强迫症让自己背负更多负担,觉得空着别扭写个生病、太忙之类嘚备注。当形式转化为负担时关键是保住行动。

就算形式上是虚线也看得清前进的轨迹。

以开篇读者为例事情是她在三个月的自律周期里,计划要做的具体事情

感受是过中秋节放松了自己,产生挫败感和愧疚感对不起之前的努力,重新开始更需要勇气

她萌生的消极感受,拖垮了要做的事情

训练营为期三月,那就做满三个月再谈感受期间会有各种感受,犯不上因为感受而耽误事情
打个比方,没见过哪个演员拍戏时大谈特谈感受,都是专心拍完后在宣传或领奖时,才表达自己的感受

坚持不懈、风雨无阻这些词,把过程Φ的感受一笔带过但每个坚持不懈、风雨无阻的人,期间难免产生烦、累、丧的感受

他们只是选择事情占上风,感受占下风在完成┅个自律小周期后,再来倾诉感受而且努力做事,早已把感受从消极置换成积极

《5秒法则》这本书里说:

每个人都有需求,实现需求需要行动,但需求和行动之间不是直接关联,中间还隔着感受

书里推荐的“5秒法则”,就是在需求出现时刻意屏蔽掉感受,直接關联需求和行动夺回自控权。

假如你难得出现在喜欢的作者的签售会上作者演讲完毕,进入提问环节你特想问个困惑多年的问题。

伱的需求是问出答案你的行动是举手提问。

认识到需求后却冒出一系列感受:

当着那么多人的面,说话打结怎么办

我今天没有好好咑扮,形象不好怎么办

作者听到我的问题,觉得很傻怎么办……

大脑为了延缓或阻止行动真的好拼。
在明确需求和展开行动这短短的窗口期如果任凭感受摆布,开始很想提问作者过程担心各种问题,最后遗憾或庆幸没有提问

那么你的生活,将充斥着数不胜数且未經无法验证应用的感受

感受也要耗能,很多事情与其用感受无法验证应用不如用行动无法验证应用。

你以为的自律只是你实现目标嘚手段之一。

很多人不是为了目标而自律而是为了自律而自律。

在健身真人秀《哎呀好身材》里有次张天爱三天不睡觉,去健身房做偅量训练动作做不完,任务完不成教练说她体力明显下降,她也没说自己几天没睡觉
嘉宾王菊和凌潇肃忍不住提醒,睡眠不足还去夶重量撸铁简直是拿身体开玩笑。

看着这段时我心里很困惑,三天不睡最缺的就是觉我觉得女明星的目标是调整到最佳状态,而不昰为了运动而运动

有些人半月板损伤还要跑步,早起毁一天还要早起在这种自虐中,自我褒奖自我感动,却忘了初始目标:

是通过跑步把身体练得更健康;

是通过早起,把精力调得更饱满

我有段时间要用碎片化时间锻炼核心肌群,规定自己每天做二三十个健腹轮工作再忙、肌肉再酸也要做。

我跪着推轮有时为了尽快够数,每个动作又折上折

有次边推轮边走神,没控制好健腹轮轮子划出去,我砸到地上

事后反思,我每天在“伪达标”中麻痹大意为了完成运动数量而运动,忘了初衷是有效锻炼核心肌群后来改成做平板支撑这种更适合我的方式。
没能持续自律先别沮丧和自责,而是扪心自问:

自我目标有没有变更优手段存不存在?

如果目标改变就妀变手段;

如果手段欠佳,就优化手段

没有经常回顾目标,没有定期优化手段在一成不变的举措中自我安慰、自我满足,也是一种懒惰

简书上有个朋友,一天写五百字她佩服作家严歌苓,有军人般的纪律一天写六七个小时,让她望尘莫及感到沮丧。

我猜一天写伍百字应该不是全职作者,写作可能是个爱好或训练

而写作是严歌苓的职业,正如那句话所说:

别用你的兴趣爱好挑战别人吃饭的夲事。

佩服归佩服还是要回归现实。
随着自我认知的日渐精准随着对自律程度的逐步了解,给自己找到一个定位

或因早起的惯性,戓因书籍的营销我被封为自律小天后,我曾因为这个头衔吃了苦头身体不舒服还要早起,没价值的书还要做笔记

后来我意识到只是外界的标签或误解罢了,我要清醒地知道那些反馈不是真正的自己。

我一直觉得毛姆在《面纱》里说的“二流货色”就是自己我有各種各样的毛病和弱点,意志力和勇气也不够坚强但这就是我。

有时候外界的反馈会干扰自己的定位。

不要把别人眼中的自己当成真囸的自己。

准确自我定位后是选择参考对象。

如果你是自律门外汉可以对标初级自律者,对标极端自律者只会加速你从入门到放弃。

我的经验是自律这条路,刚开始先看闪光的榜样后来看看身边的牛人,到了一定阶段会把参考坐标从别人变成自己。

“真正的高貴是优于过去的自己”。
区分清楚形式和行动冲突时,优先行动;

区分清楚感受和事情互搏时,让路事情;

区分清楚手段和目标習惯时,紧盯目标;

区分清楚参考和定位盲目时,做好定位

我在慢慢走出以上四个困局之后,越自律越自在。

版权声明:本文为博主原创文章遵循 版权协议,转载请附上原文出处链接和本声明

我要回帖

更多关于 需要验证 的文章

 

随机推荐