python多线程与多进程及其区别
<p>个人一直觉得对学习任何知识而言,概念是相当重要的。掌握了概念和原理,细节可以留给实践去推敲。掌握的关键在于理解,通过具体的实例和实际操作来感性的体会概念和原理可以起到很好的效果。本文通过一些具体的例子简单介绍一下python的多线程和多进程,后续会写一些进程通信和线程通信的一些文章。</p><h3>python多线程</h3>
<p>python中提供两个标准库thread和threading用于对线程的支持,python3中已放弃对前者的支持,后者是一种更高层次封装的线程库,接下来均以后者为例。</p>
<h4>创建线程</h4>
<p>python中有两种方式实现线程:</p>
<ol>
<li><span class="fontstyle0">实例化一个threading.Thread的对象,并传入一个初始化函数对象(initial function )作为线程执行的入口;</span></li>
<li>继承threading.Thread,并重写run函数;</li>
</ol>
<ul>
<li>方式1:创建threading.Thread对象</li>
</ul>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 255, 1)">import</span><span style="color: rgba(0, 0, 0, 1)"> threading
</span><span style="color: rgba(0, 0, 255, 1)">import</span><span style="color: rgba(0, 0, 0, 1)"> time
</span><span style="color: rgba(0, 0, 255, 1)">def</span><span style="color: rgba(0, 0, 0, 1)"> tstart(arg):
time.sleep(</span>0.5<span style="color: rgba(0, 0, 0, 1)">)
</span><span style="color: rgba(0, 0, 255, 1)">print</span>(<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">%s running....</span><span style="color: rgba(128, 0, 0, 1)">"</span> %<span style="color: rgba(0, 0, 0, 1)"> arg)
</span><span style="color: rgba(0, 0, 255, 1)">if</span> <span style="color: rgba(128, 0, 128, 1)">__name__</span> == <span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(128, 0, 0, 1)">__main__</span><span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(0, 0, 0, 1)">:
t1 </span>= threading.Thread(target=tstart, args=(<span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(128, 0, 0, 1)">This is thread 1</span><span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(0, 0, 0, 1)">,))
t2 </span>= threading.Thread(target=tstart, args=(<span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(128, 0, 0, 1)">This is thread 2</span><span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(0, 0, 0, 1)">,))
t1.start()
t2.start()
</span><span style="color: rgba(0, 0, 255, 1)">print</span>(<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">This is main function</span><span style="color: rgba(128, 0, 0, 1)">"</span>)</pre>
</div>
<p>结果:</p>
<div class="cnblogs_code"><img id="code_img_closed_1c625139-7495-40d2-8993-b440722a1b7d" class="code_img_closed lazyload" alt="" data-src="http://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif"><img id="code_img_opened_1c625139-7495-40d2-8993-b440722a1b7d" class="code_img_opened lazyload" style="display: none" alt="" data-src="http://images.cnblogs.com/OutliningIndicators/ExpandedBlockStart.gif">
<div id="cnblogs_code_open_1c625139-7495-40d2-8993-b440722a1b7d" class="cnblogs_code_hide">
<pre>This <span style="color: rgba(0, 0, 255, 1)">is</span><span style="color: rgba(0, 0, 0, 1)"> main function
This </span><span style="color: rgba(0, 0, 255, 1)">is</span> thread 2<span style="color: rgba(0, 0, 0, 1)"> running....
This </span><span style="color: rgba(0, 0, 255, 1)">is</span> thread 1 running....</pre>
</div>
<span class="cnblogs_code_collapse">View Code</span></div>
<ul>
<li>方式2:继承threading.Thread,并重写run</li>
</ul>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 255, 1)">import</span><span style="color: rgba(0, 0, 0, 1)"> threading
</span><span style="color: rgba(0, 0, 255, 1)">import</span><span style="color: rgba(0, 0, 0, 1)"> time
</span><span style="color: rgba(0, 0, 255, 1)">class</span><span style="color: rgba(0, 0, 0, 1)"> CustomThread(threading.Thread):
</span><span style="color: rgba(0, 0, 255, 1)">def</span> <span style="color: rgba(128, 0, 128, 1)">__init__</span><span style="color: rgba(0, 0, 0, 1)">(self, thread_name):
</span><span style="color: rgba(0, 128, 0, 1)">#</span><span style="color: rgba(0, 128, 0, 1)"> step 1: call base __init__ function</span>
super(CustomThread, self).<span style="color: rgba(128, 0, 128, 1)">__init__</span>(name=<span style="color: rgba(0, 0, 0, 1)">thread_name)
self._tname </span>=<span style="color: rgba(0, 0, 0, 1)"> thread_name
</span><span style="color: rgba(0, 0, 255, 1)">def</span><span style="color: rgba(0, 0, 0, 1)"> run(self):
</span><span style="color: rgba(0, 128, 0, 1)">#</span><span style="color: rgba(0, 128, 0, 1)"> step 2: overide run function</span>
time.sleep(0.5<span style="color: rgba(0, 0, 0, 1)">)
</span><span style="color: rgba(0, 0, 255, 1)">print</span>(<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">This is %s running....</span><span style="color: rgba(128, 0, 0, 1)">"</span> %<span style="color: rgba(0, 0, 0, 1)"> self._tname)
</span><span style="color: rgba(0, 0, 255, 1)">if</span> <span style="color: rgba(128, 0, 128, 1)">__name__</span> == <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">__main__</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">:
t1 </span>= CustomThread(<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">thread 1</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">)
t2 </span>= CustomThread(<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">thread 2</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">)
t1.start()
t2.start()
</span><span style="color: rgba(0, 0, 255, 1)">print</span>(<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">This is main function</span><span style="color: rgba(128, 0, 0, 1)">"</span>)</pre>
</div>
<p> 执行结果同方式1.</p>
<h4>threading.Thread</h4>
<p>上面两种方法本质上都是直接或者间接使用threading.Thread类</p>
<blockquote>
<p><code class="descclassname">threading.</code><code class="descname">Thread</code><span class="sig-paren">(<em>group=None</em>, <em>target=None</em>, <em>name=None</em>, <em>args=()</em>, <em>kwargs={}</em><span class="sig-paren">)</span></span></p>
</blockquote>
<p>关联上面两种创建线程的方式:</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 255, 1)">import</span><span style="color: rgba(0, 0, 0, 1)"> threading
</span><span style="color: rgba(0, 0, 255, 1)">import</span><span style="color: rgba(0, 0, 0, 1)"> time
</span><span style="color: rgba(0, 0, 255, 1)">class</span><span style="color: rgba(0, 0, 0, 1)"> CustomThread(threading.Thread):
</span><span style="color: rgba(0, 0, 255, 1)">def</span> <span style="color: rgba(128, 0, 128, 1)">__init__</span>(self, thread_name, target =<span style="color: rgba(0, 0, 0, 1)"> None):
</span><span style="color: rgba(0, 128, 0, 1)">#</span><span style="color: rgba(0, 128, 0, 1)"> step 1: call base __init__ function</span>
super(CustomThread, self).<span style="color: rgba(128, 0, 128, 1)">__init__</span>(name=thread_name, target=target, args =<span style="color: rgba(0, 0, 0, 1)"> (thread_name,))
self._tname </span>=<span style="color: rgba(0, 0, 0, 1)"> thread_name
</span><span style="color: rgba(0, 0, 255, 1)">def</span><span style="color: rgba(0, 0, 0, 1)"> run(self):
</span><span style="color: rgba(0, 128, 0, 1)">#</span><span style="color: rgba(0, 128, 0, 1)"> step 2: overide run function</span>
<span style="color: rgba(0, 128, 0, 1)">#</span><span style="color: rgba(0, 128, 0, 1)"> time.sleep(0.5)</span>
<span style="color: rgba(0, 128, 0, 1)">#</span><span style="color: rgba(0, 128, 0, 1)"> print("This is %s running....@run" % self._tname)</span>
<span style="color: rgba(0, 0, 0, 1)"> super(CustomThread, self).run()
</span><span style="color: rgba(0, 0, 255, 1)">def</span><span style="color: rgba(0, 0, 0, 1)"> target(arg):
time.sleep(</span>0.5<span style="color: rgba(0, 0, 0, 1)">)
</span><span style="color: rgba(0, 0, 255, 1)">print</span>(<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">This is %s running....@target</span><span style="color: rgba(128, 0, 0, 1)">"</span> %<span style="color: rgba(0, 0, 0, 1)"> arg)
</span><span style="color: rgba(0, 0, 255, 1)">if</span> <span style="color: rgba(128, 0, 128, 1)">__name__</span> == <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">__main__</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">:
t1 </span>= CustomThread(<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">thread 1</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">, target)
t2 </span>= CustomThread(<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">thread 2</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">, target)
t1.start()
t2.start()
</span><span style="color: rgba(0, 0, 255, 1)">print</span>(<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">This is main function</span><span style="color: rgba(128, 0, 0, 1)">"</span>)</pre>
</div>
<p>结果:</p>
<div class="cnblogs_code">
<pre>This is<span> main function
This is thread 1<span> running....@target
This is thread 2 running....@target</span></span></pre>
</div>
<p>上面这段代码说明:</p>
<ol>
<li>两种方式创建线程,指定的参数最终都会传给threading.Thread类;</li>
<li>传给线程的目标函数是在基类Thread的run函数体中被调用的,如果run没有被重写的话。</li>
</ol>
<p>threading模块的一些属性和方法可以参照官网,这里重点介绍一下threading.Thread对象的方法</p>
<p>下面是threading.Thread提供的线程对象方法和属性:</p>
<blockquote>
<ul>
<li>start():创建线程后通过start启动线程,等待CPU调度,为run函数执行做准备;</li>
<li>run():线程开始执行的入口函数,函数体中会调用用户编写的target函数,或者执行被重载的run函数;</li>
<li>join():阻塞挂起调用该函数的线程,直到被调用线程执行完成或超时。通常会在主线程中调用该方法,等待其他线程执行完成。</li>
<li>name、getName()&setName():线程名称相关的操作;</li>
<li>ident:整数类型的线程标识符,线程开始执行前(调用start之前)为None;</li>
<li>isAlive()、is_alive():start函数执行之后到run函数执行完之前都为True;</li>
<li>daemon、isDaemon()&setDaemon():守护线程相关;</li>
</ul>
</blockquote>
<p>这些是我们创建线程之后通过线程对象对线程进行管理和获取线程信息的方法。</p>
<h4> 多线程执行</h4>
<p> 在主线程中创建若线程之后,他们之间没有任何协作和同步,除主线程之外每个线程都是从run开始被执行,直到执行完毕。</p>
<p><strong>join</strong></p>
<p>我们可以通过join方法让主线程阻塞,等待其创建的线程执行完成。</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 255, 1)">import</span><span style="color: rgba(0, 0, 0, 1)"> threading
</span><span style="color: rgba(0, 0, 255, 1)">import</span><span style="color: rgba(0, 0, 0, 1)"> time
</span><span style="color: rgba(0, 0, 255, 1)">def</span><span style="color: rgba(0, 0, 0, 1)"> tstart(arg):
</span><span style="color: rgba(0, 0, 255, 1)">print</span>(<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">%s running....at: %s</span><span style="color: rgba(128, 0, 0, 1)">"</span> %<span style="color: rgba(0, 0, 0, 1)"> (arg,time.time()))
time.sleep(</span>1<span style="color: rgba(0, 0, 0, 1)">)
</span><span style="color: rgba(0, 0, 255, 1)">print</span>(<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">%s is finished! at: %s</span><span style="color: rgba(128, 0, 0, 1)">"</span> %<span style="color: rgba(0, 0, 0, 1)"> (arg,time.time()))
</span><span style="color: rgba(0, 0, 255, 1)">if</span> <span style="color: rgba(128, 0, 128, 1)">__name__</span> == <span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(128, 0, 0, 1)">__main__</span><span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(0, 0, 0, 1)">:
t1 </span>= threading.Thread(target=tstart, args=(<span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(128, 0, 0, 1)">This is thread 1</span><span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(0, 0, 0, 1)">,))
t1.start()
t1.join() </span><span style="color: rgba(0, 128, 0, 1)">#</span><span style="color: rgba(0, 128, 0, 1)"> 当前线程阻塞,等待t1线程执行完成</span>
<span style="color: rgba(0, 0, 255, 1)">print</span>(<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">This is main function at:%s</span><span style="color: rgba(128, 0, 0, 1)">"</span> % time.time())</pre>
</div>
<p>结果:</p>
<div class="cnblogs_code">
<pre>This <span style="color: rgba(0, 0, 255, 1)">is</span> thread 1 running....at: 1564906617.43<span style="color: rgba(0, 0, 0, 1)">
This </span><span style="color: rgba(0, 0, 255, 1)">is</span> thread 1 <span style="color: rgba(0, 0, 255, 1)">is</span> finished! at: 1564906618.43<span style="color: rgba(0, 0, 0, 1)">
This </span><span style="color: rgba(0, 0, 255, 1)">is</span> main function at:1564906618.43</pre>
</div>
<p>如果不加任何限制,当主线程执行完毕之后,当前程序并不会结束,必须等到所有线程都结束之后才能结束当前进程。</p>
<p>将上面程序中的t1.join()去掉,执行结果如下:</p>
<div class="cnblogs_code">
<pre>This is thread 1 running....at: 1564906769.52<span>
This is main function at:1564906769.52<span>
This is thread 1 is finished! at: 1564906770.52</span></span></pre>
</div>
<p>可以通过将创建的线程指定为守护线程(daemon),这样主线程执行完毕之后会立即结束未执行完的线程,然后结束程序。</p>
<p><strong>deamon守护线程</strong></p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 255, 1)">import</span><span style="color: rgba(0, 0, 0, 1)"> threading
</span><span style="color: rgba(0, 0, 255, 1)">import</span><span style="color: rgba(0, 0, 0, 1)"> time
</span><span style="color: rgba(0, 0, 255, 1)">def</span><span style="color: rgba(0, 0, 0, 1)"> tstart(arg):
</span><span style="color: rgba(0, 0, 255, 1)">print</span>(<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">%s running....at: %s</span><span style="color: rgba(128, 0, 0, 1)">"</span> %<span style="color: rgba(0, 0, 0, 1)"> (arg,time.time()))
time.sleep(</span>1<span style="color: rgba(0, 0, 0, 1)">)
</span><span style="color: rgba(0, 0, 255, 1)">print</span>(<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">%s is finished! at: %s</span><span style="color: rgba(128, 0, 0, 1)">"</span> %<span style="color: rgba(0, 0, 0, 1)"> (arg,time.time()))
</span><span style="color: rgba(0, 0, 255, 1)">if</span> <span style="color: rgba(128, 0, 128, 1)">__name__</span> == <span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(128, 0, 0, 1)">__main__</span><span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(0, 0, 0, 1)">:
t1 </span>= threading.Thread(target=tstart, args=(<span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(128, 0, 0, 1)">This is thread 1</span><span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(0, 0, 0, 1)">,))
t1.setDaemon(True)
t1.start()
</span><span style="color: rgba(0, 128, 0, 1)">#</span><span style="color: rgba(0, 128, 0, 1)"> t1.join() # 当前线程阻塞,等待t1线程执行完成</span>
<span style="color: rgba(0, 0, 255, 1)">print</span>(<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">This is main function at:%s</span><span style="color: rgba(128, 0, 0, 1)">"</span> % time.time())</pre>
</div>
<p>结果:</p>
<div class="cnblogs_code">
<pre>This is thread 1 running....at: 1564906847.85<span>
This is main function at:1564906847.85</span></pre>
</div>
<h3>python多进程</h3>
<p>相比较于threading模块用于创建python多线程,python提供multiprocessing用于创建多进程。先看一下创建进程的两种方式。</p>
<blockquote>
<p>The <code class="xref py py-mod docutils literal">multiprocessing</code> package mostly replicates the API of the <code class="xref py py-mod docutils literal">threading</code> module. —— python doc</p>
</blockquote>
<p><strong>创建进程</strong></p>
<p>创建进程的方式和创建线程的方式类似:</p>
<ol>
<li><span class="fontstyle0">实例化一个multiprocessing.Process的对象,并传入一个初始化函数对象(initial function )作为新建进程执行入口;</span></li>
<li>继承multiprocessing.Process,并重写run函数;</li>
</ol>
<ul>
<li>方式1:</li>
</ul>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 255, 1)">from</span> multiprocessing <span style="color: rgba(0, 0, 255, 1)">import</span><span style="color: rgba(0, 0, 0, 1)"> Process
</span><span style="color: rgba(0, 0, 255, 1)">import</span><span style="color: rgba(0, 0, 0, 1)"> os, time
</span><span style="color: rgba(0, 0, 255, 1)">def</span><span style="color: rgba(0, 0, 0, 1)"> pstart(name):
</span><span style="color: rgba(0, 128, 0, 1)">#</span><span style="color: rgba(0, 128, 0, 1)"> time.sleep(0.1)</span>
<span style="color: rgba(0, 0, 255, 1)">print</span>(<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">Process name: %s, pid: %s </span><span style="color: rgba(128, 0, 0, 1)">"</span>%<span style="color: rgba(0, 0, 0, 1)">(name, os.getpid()))
</span><span style="color: rgba(0, 0, 255, 1)">if</span> <span style="color: rgba(128, 0, 128, 1)">__name__</span> == <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">__main__</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">:
subproc </span>= Process(target=pstart, args=(<span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(128, 0, 0, 1)">subprocess</span><span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(0, 0, 0, 1)">,))
subproc.start()
subproc.join()
</span><span style="color: rgba(0, 0, 255, 1)">print</span>(<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">subprocess pid: %s</span><span style="color: rgba(128, 0, 0, 1)">"</span>%<span style="color: rgba(0, 0, 0, 1)">subproc.pid)
</span><span style="color: rgba(0, 0, 255, 1)">print</span>(<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">current process pid: %s</span><span style="color: rgba(128, 0, 0, 1)">"</span> % os.getpid())</pre>
</div>
<p>结果:</p>
<div class="cnblogs_code">
<pre>Process name: subprocess, pid: 4888<span style="color: rgba(0, 0, 0, 1)">
subprocess pid: </span>4888<span style="color: rgba(0, 0, 0, 1)">
current process pid: </span>9912</pre>
</div>
<ul>
<li>方式2:</li>
</ul>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 255, 1)">from</span> multiprocessing <span style="color: rgba(0, 0, 255, 1)">import</span><span style="color: rgba(0, 0, 0, 1)"> Process
</span><span style="color: rgba(0, 0, 255, 1)">import</span><span style="color: rgba(0, 0, 0, 1)"> os, time
</span><span style="color: rgba(0, 0, 255, 1)">class</span><span style="color: rgba(0, 0, 0, 1)"> CustomProcess(Process):
</span><span style="color: rgba(0, 0, 255, 1)">def</span> <span style="color: rgba(128, 0, 128, 1)">__init__</span>(self, p_name, target=<span style="color: rgba(0, 0, 0, 1)">None):
</span><span style="color: rgba(0, 128, 0, 1)">#</span><span style="color: rgba(0, 128, 0, 1)"> step 1: call base __init__ function()</span>
super(CustomProcess, self).<span style="color: rgba(128, 0, 128, 1)">__init__</span>(name=p_name, target=target, args=<span style="color: rgba(0, 0, 0, 1)">(p_name,))
</span><span style="color: rgba(0, 0, 255, 1)">def</span><span style="color: rgba(0, 0, 0, 1)"> run(self):
</span><span style="color: rgba(0, 128, 0, 1)">#</span><span style="color: rgba(0, 128, 0, 1)"> step 2:</span>
<span style="color: rgba(0, 128, 0, 1)">#</span><span style="color: rgba(0, 128, 0, 1)"> time.sleep(0.1)</span>
<span style="color: rgba(0, 0, 255, 1)">print</span>(<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">Custom Process name: %s, pid: %s </span><span style="color: rgba(128, 0, 0, 1)">"</span>%<span style="color: rgba(0, 0, 0, 1)">(self.name, os.getpid()))
</span><span style="color: rgba(0, 0, 255, 1)">if</span> <span style="color: rgba(128, 0, 128, 1)">__name__</span> == <span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(128, 0, 0, 1)">__main__</span><span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(0, 0, 0, 1)">:
p1 </span>= CustomProcess(<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">process_1</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">)
p1.start()
p1.join()
</span><span style="color: rgba(0, 0, 255, 1)">print</span>(<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">subprocess pid: %s</span><span style="color: rgba(128, 0, 0, 1)">"</span>%<span style="color: rgba(0, 0, 0, 1)">p1.pid)
</span><span style="color: rgba(0, 0, 255, 1)">print</span>(<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">current process pid: %s</span><span style="color: rgba(128, 0, 0, 1)">"</span> % os.getpid())</pre>
</div>
<p>这里可以思考一下,如果像多线程一样,存在一个全局的变量share_data,不同进程同时访问share_data会有问题吗?</p>
<p>由于每一个进程拥有独立的内存地址空间且互相隔离,因此不同进程看到的share_data是不同的、分别位于不同的地址空间,同时访问不会有问题。这里需要注意一下。</p>
<p><strong>Subprocess模块</strong></p>
<p>既然说道了多进程,那就顺便提一下另一种创建进程的方式。</p>
<p>python提供了Sunprocess模块可以在程序执行过程中,调用外部的程序。</p>
<p>如我们可以在python程序中打开记事本,打开cmd,或者在某个时间点关机:</p>
<div class="cnblogs_code">
<pre>>>> <span style="color: rgba(0, 0, 255, 1)">import</span><span style="color: rgba(0, 0, 0, 1)"> subprocess
</span>>>> subprocess.Popen([<span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(128, 0, 0, 1)">cmd</span><span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(0, 0, 0, 1)">])
</span><subprocess.Popen object at 0x0339F550>
>>> subprocess.Popen([<span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(128, 0, 0, 1)">notepad</span><span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(0, 0, 0, 1)">])
</span><subprocess.Popen object at 0x03262B70>
>>> subprocess.Popen([<span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(128, 0, 0, 1)">shutdown</span><span style="color: rgba(128, 0, 0, 1)">'</span>, <span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(128, 0, 0, 1)">-p</span><span style="color: rgba(128, 0, 0, 1)">'</span>])</pre>
</div>
<p>或者使用ping测试一下网络连通性:</p>
<div class="cnblogs_code">
<pre>>>> res = subprocess.Popen([<span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(128, 0, 0, 1)">ping</span><span style="color: rgba(128, 0, 0, 1)">'</span>, <span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(128, 0, 0, 1)">www.cnblogs.com</span><span style="color: rgba(128, 0, 0, 1)">'</span>], stdout=<span style="color: rgba(0, 0, 0, 1)">subprocess.PIPE).communicate()
</span>>>> <span style="color: rgba(0, 0, 255, 1)">print</span><span style="color: rgba(0, 0, 0, 1)"> res
正在 Ping www.cnblogs.com [</span>101.37.113.127] 具有 32<span style="color: rgba(0, 0, 0, 1)"> 字节的数据:</span></pre>
<p>来自 101.37.113.127 的回复: 字节=32 时间=1ms TTL=91 <br>来自 101.37.113.127 的回复: 字节=32 时间=1ms TTL=91
<br>来自 101.37.113.127 的回复: 字节=32 时间=1ms TTL=91
<br>来自 101.37.113.127 的回复: 字节=32 时间=1ms TTL=91
<br>
<br>101.37.113.127 的 Ping 统计信息:
<br> 数据包: 已发送 = 4,已接收 = 4,丢失 = 0 (0% 丢失),
<br>往返行程的估计时间(以毫秒为单位):
<br> 最短 = 1ms,最长 = 1ms,平均 = 1ms</p>
</div>
<h3>python多线程与多进程比较</h3>
<p>先来看两个例子:</p>
<p>开启两个python线程分别做一亿次加一操作,和单独使用一个线程做一亿次加一操作:</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 255, 1)">def</span><span style="color: rgba(0, 0, 0, 1)"> tstart(arg):
var </span>=<span style="color: rgba(0, 0, 0, 1)"> 0
</span><span style="color: rgba(0, 0, 255, 1)">for</span> i <span style="color: rgba(0, 0, 255, 1)">in</span> xrange(100000000<span style="color: rgba(0, 0, 0, 1)">):
var </span>+= 1
<span style="color: rgba(0, 0, 255, 1)">if</span> <span style="color: rgba(128, 0, 128, 1)">__name__</span> == <span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(128, 0, 0, 1)">__main__</span><span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(0, 0, 0, 1)">:
t1 </span>= threading.Thread(target=tstart, args=(<span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(128, 0, 0, 1)">This is thread 1</span><span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(0, 0, 0, 1)">,))
t2 </span>= threading.Thread(target=tstart, args=(<span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(128, 0, 0, 1)">This is thread 2</span><span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(0, 0, 0, 1)">,))
start_time </span>=<span style="color: rgba(0, 0, 0, 1)"> time.time()
t1.start()
t2.start()
t1.join()
t2.join()
</span><span style="color: rgba(0, 0, 255, 1)">print</span>(<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">Two thread cost time: %s</span><span style="color: rgba(128, 0, 0, 1)">"</span> % (time.time() -<span style="color: rgba(0, 0, 0, 1)"> start_time))
start_time </span>=<span style="color: rgba(0, 0, 0, 1)"> time.time()
tstart(</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">This is thread 0</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">)
</span><span style="color: rgba(0, 0, 255, 1)">print</span>(<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">Main thread cost time: %s</span><span style="color: rgba(128, 0, 0, 1)">"</span> % (time.time() - start_time))</pre>
</div>
<p>结果:</p>
<div class="cnblogs_code">
<pre>Two thread cost time: 20.6570000648<span style="color: rgba(0, 0, 0, 1)">
Main thread cost time: </span>2.52800011635</pre>
</div>
<p>上面的例子如果只开启t1和t2两个线程中的一个,那么运行时间和主线程基本一致。这个后面会解释原因。</p>
<p>使用两个进程进行上面的操作:</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 255, 1)">def</span><span style="color: rgba(0, 0, 0, 1)"> pstart(arg):
var </span>=<span style="color: rgba(0, 0, 0, 1)"> 0
</span><span style="color: rgba(0, 0, 255, 1)">for</span> i <span style="color: rgba(0, 0, 255, 1)">in</span> xrange(100000000<span style="color: rgba(0, 0, 0, 1)">):
var </span>+= 1
<span style="color: rgba(0, 0, 255, 1)">if</span> <span style="color: rgba(128, 0, 128, 1)">__name__</span> == <span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(128, 0, 0, 1)">__main__</span><span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(0, 0, 0, 1)">:
p1 </span>= Process(target = pstart, args = (<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">1</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">, ))
p2 </span>= Process(target = pstart, args = (<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">2</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">, ))
start_time </span>=<span style="color: rgba(0, 0, 0, 1)"> time.time()
p1.start()
p2.start()
p1.join()
p2.join()
</span><span style="color: rgba(0, 0, 255, 1)">print</span>(<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">Two process cost time: %s</span><span style="color: rgba(128, 0, 0, 1)">"</span> % (time.time() -<span style="color: rgba(0, 0, 0, 1)"> start_time))
start_time </span>=<span style="color: rgba(0, 0, 0, 1)"> time.time()
pstart(</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">0</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">)
</span><span style="color: rgba(0, 0, 255, 1)">print</span>(<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">Current process cost time: %s</span><span style="color: rgba(128, 0, 0, 1)">"</span> % (time.time() - start_time))</pre>
</div>
<p>结果:</p>
<div class="cnblogs_code">
<pre>Two process cost time: 2.91599988937<span style="color: rgba(0, 0, 0, 1)">
Current process cost time: </span>2.52400016785</pre>
</div>
<p><strong>对比分析</strong></p>
<p>双进程并行执行和单进程执行相同的运算代码,耗时基本相同,双进程耗时会稍微多一些,可能的原因是进程创建和销毁会进行系统调用,造成额外的时间开销。</p>
<p>但是对于python线程,双线程并行执行耗时比单线程要高的多,效率相差近10倍。如果将两个并行线程改成串行执行,即:</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 0, 1)"> t1.start()
t1.join()
t2.start()
t2.join()
</span><span style="color: rgba(0, 128, 0, 1)">#</span><span style="color: rgba(0, 128, 0, 1)">Two thread cost time: 5.12199997902</span>
<span style="color: rgba(0, 128, 0, 1)">#</span><span style="color: rgba(0, 128, 0, 1)">Main thread cost time: 2.54200005531</span></pre>
</div>
<p>可以看到三个线程串行执行,每一个执行的时间基本相同。</p>
<p>本质原因双线程是并发执行的,而不是真正的并行执行。原因就在于GIL锁。</p>
<p><strong>GIL锁</strong></p>
<p>提起python多线程就不得不提一下GIL(Global Interpreter Lock 全局解释器锁),这是目前占统治地位的python解释器CPython中为了保证数据安全所实现的一种锁。不管进程中有多少线程,只有拿到了GIL锁的线程才可以在CPU上运行,即时是多核处理器。对一个进程而言,不管有多少线程,任一时刻,只会有一个线程在执行。对于CPU密集型的线程,其效率不仅仅不高,反而有可能比较低。python多线程比较适用于IO密集型的程序。对于的确需要并行运行的程序,可以考虑多进程。</p>
<p>多线程对锁的争夺,CPU对线程的调度,线程之间的切换等均会有时间开销。</p>
<h3>线程与进程区别</h3>
<p>下面简单的比较一下线程与进程</p>
<ul>
<li>进程是资源分配的基本单位,线程是CPU执行和调度的基本单位;</li>
<li>通信/同步方式:</li>
<ul>
<li>进程:
<ul>
<li>通信方式:管道,FIFO,消息队列,信号,共享内存,socket,stream流;</li>
<li>同步方式:PV信号量,管程</li>
</ul>
</li>
<li>线程:
<ul>
<li>同步方式:互斥锁,递归锁,条件变量,信号量</li>
<li>通信方式:位于同一进程的线程共享进程资源,因此线程间没有类似于进程间用于数据传递的通信方式,线程间的通信主要是用于线程同步。</li>
</ul>
</li>
</ul>
<li>CPU上真正执行的是线程,线程比进程轻量,其切换和调度代价比进程要小;</li>
<li>线程间对于共享的进程数据需要考虑线程安全问题,由于进程之间是隔离的,拥有独立的内存空间资源,相对比较安全,只能通过上面列出的IPC(Inter-Process Communication)进行数据传输;</li>
<li>系统有一个个进程组成,每个进程包含代码段、数据段、堆空间和栈空间,以及操作系统共享部分 ,有等待,就绪和运行三种状态;</li>
<li>一个进程可以包含多个线程,线程之间共享进程的资源(文件描述符、全局变量、堆空间等),寄存器变量和栈空间等是线程私有的;</li>
<li>操作系统中一个进程挂掉不会影响其他进程,如果一个进程中的某个线程挂掉而且OS对线程的支持是多对一模型,那么会导致当前进程挂掉;</li>
<li>如果CPU和系统支持多线程与多进程,多个进程并行执行的同时,每个进程中的线程也可以并行执行,这样才能最大限度的榨取硬件的性能;</li>
</ul>
<h3>线程和进程的上下文切换</h3>
<p>进程切换过程切换牵涉到非常多的东西,寄存器内容保存到任务状态段TSS,切换页表,堆栈等。简单来说可以分为下面两步:</p>
<ol>
<li>页全局目录切换,使CPU到新进程的线性地址空间寻址;</li>
<li>切换内核态堆栈和硬件上下文,硬件上下文包含CPU寄存器的内容,存放在TSS中;</li>
</ol>
<p>线程运行于进程地址空间,切换过程不涉及到空间的变换,只牵涉到第二步;</p>
<h3>使用多线程还是多进程?</h3>
<p>CPU密集型:程序需要占用CPU进行大量的运算和数据处理;</p>
<p>I/O密集型:程序中需要频繁的进行I/O操作;例如网络中socket数据传输和读取等;</p>
<p>由于python多线程并不是并行执行,因此较适合与I/O密集型程序,多进程并行执行适用于CPU密集型程序;</p>
</div>
<div id="MySignature" role="contentinfo">
欢迎转载博客文章,转载请标明出处!<br><br>
来源:https://www.cnblogs.com/yssjun/p/11302500.html
頁:
[1]