赵山菊 發表於 2022-1-18 20:44:00

Python中的多线程

<h2>什么是多线程:</h2>
<p>  进程:正在运行的程序,QQ 360 ......</p>
<p>&nbsp; &nbsp; &nbsp; &nbsp;线程:就是进程中一条执行程序的执行路径,一个程序至少有一条执行路径。(360中的杀毒 电脑体检 电脑清理 同时运行的话就需要开启多条路径)</p>
<p>  每个线程都有自己需要运行的内容,而这些内容可以称为线程要执行的任务。</p>
<p>  开启多线程是为了同时运行多部分代码。</p>
<p>  好处:解决了多部分需要同时运行的问题</p>
<p>  弊端:如果线程过多,会导致效率很低(因为程序的执行都是CPU做着随机 快速切换来完成的)</p>
<ul>
<li>线程与进程的区别
<ul>
<li>线程共享内存,进程独立内存</li>
<li>线程启动速度块,进程启动速度慢,运行时速度没有可比性</li>
<li>同一个进程的线程间可以直接交流,两个进程想通信,必须通过一个中间代理来实现</li>
<li>创建新线程很简单,创建新进程需要对其父进程进行一次克隆</li>
<li>一个线程可以控制和操作同一线程里的其他线程,但是进程只能操作子进程</li>
</ul>
</li>
</ul>
<p>&nbsp;</p>
<h2>threading模块</h2>
<ul>
<li><span style="font-size: 16px"><strong>多线程的使用方式一:直接使用</strong></span>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 128, 0, 1)">#</span><span style="color: rgba(0, 128, 0, 1)"> -*- coding:utf-8 -*-</span><span style="color: rgba(0, 128, 0, 1)">
#</span><span style="color: rgba(0, 128, 0, 1)"> 线程使用的方式一</span>
<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, 128, 0, 1)">#</span><span style="color: rgba(0, 128, 0, 1)"> 需要多线程运行的函数</span>
<span style="color: rgba(0, 0, 255, 1)">def</span><span style="color: rgba(0, 0, 0, 1)"> fun(args):
    </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</span><span style="color: rgba(128, 0, 0, 1)">"</span> %<span style="color: rgba(0, 0, 0, 1)"> args)
    time.sleep(</span>2<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运行结束</span><span style="color: rgba(128, 0, 0, 1)">"</span> %<span style="color: rgba(0, 0, 0, 1)"> args)


</span><span style="color: rgba(0, 128, 0, 1)">#</span><span style="color: rgba(0, 128, 0, 1)"> 创建线程</span>
t1 = threading.Thread(target=fun, args=(1<span style="color: rgba(0, 0, 0, 1)">,))
t2 </span>= threading.Thread(target=fun, args=(2<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()
end_time </span>=<span style="color: rgba(0, 0, 0, 1)"> time.time()
</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)">两个线程一共的运行时间为:</span><span style="color: rgba(128, 0, 0, 1)">"</span>, end_time-<span style="color: rgba(0, 0, 0, 1)">start_time)
</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)">主线程结束</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">)

</span><span style="color: rgba(128, 0, 0, 1)">"""</span><span style="color: rgba(128, 0, 0, 1)">
运行结果:
我是线程1
我是线程2两个线程一共的运行时间为: 0.0010077953338623047
主线程结束

线程1运行结束
线程2运行结束
</span><span style="color: rgba(128, 0, 0, 1)">"""</span></pre>
</div>
</li>
<li><strong><span style="font-size: 16px">线程的第二种使用方式:继承式调用</span></strong>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 128, 0, 1)">#</span><span style="color: rgba(0, 128, 0, 1)"> 继承式调用</span>
<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)"> MyThreading(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, name):
      super(MyThreading, self).</span><span style="color: rgba(128, 0, 128, 1)">__init__</span><span style="color: rgba(0, 0, 0, 1)">()
      self.name </span>=<span style="color: rgba(0, 0, 0, 1)"> name

    </span><span style="color: rgba(0, 128, 0, 1)">#</span><span style="color: rgba(0, 128, 0, 1)"> 线程要运行的代码</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, 0, 255, 1)">print</span>(<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">我是线程%s</span><span style="color: rgba(128, 0, 0, 1)">"</span> %<span style="color: rgba(0, 0, 0, 1)"> self.name)
      time.sleep(</span>2<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运行结束</span><span style="color: rgba(128, 0, 0, 1)">"</span> %<span style="color: rgba(0, 0, 0, 1)"> self.name)


t1 </span>= MyThreading(1<span style="color: rgba(0, 0, 0, 1)">)
t2 </span>= MyThreading(2<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()
end_time </span>=<span style="color: rgba(0, 0, 0, 1)"> time.time()
</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)">两个线程一共的运行时间为:</span><span style="color: rgba(128, 0, 0, 1)">"</span>, end_time-<span style="color: rgba(0, 0, 0, 1)">start_time)
</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)">主线程结束</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">)

</span><span style="color: rgba(128, 0, 0, 1)">"""</span><span style="color: rgba(128, 0, 0, 1)">
运行结果:
我是线程1
我是线程2
两个线程一共的运行时间为: 0.0010724067687988281
主线程结束
线程2运行结束
线程1运行结束
</span><span style="color: rgba(128, 0, 0, 1)">"""</span></pre>
</div>
</li>
</ul>
<h2>守护线程与join方法</h2>
<ul>
<li>在Python多线程编程中,join方法的作用式线程同步。</li>
<li>守护线程,是为守护别人而存在的,当设置为守护线程后,被守护的主线程不存在后,守护线程也自然不存在。</li>
</ul>
<p>  </p>
<ul>
<li>第一种:python多线程默认情况
<ul>
<li>Python多线程默认情况(设置线程setDaemon(False)),主线程执行完自己的任务后,就退出了,此时子线程会继续执行自己的任务,直到子线程任务结束</li>
<li>代码演示:threading中的两个创建多线成的例子都是。</li>
</ul>
</li>
<li>第二种:开启守护线程
<ul>
<li>开启线程的setDaemon(True)),设置子线程为守护线程,实现主程序结束,子程序立马全部结束功能</li>
<li>代码演示:
<div class="cnblogs_code"><img src="http://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif"><img id="code_img_opened_85da574c-fcc6-4a20-a003-ce9d52575da1" class="code_img_opened lazyload" style="display: none" data-src="http://images.cnblogs.com/OutliningIndicators/ExpandedBlockStart.gif">
<div id="cnblogs_code_open_85da574c-fcc6-4a20-a003-ce9d52575da1" class="cnblogs_code_hide">
<pre><span style="color: rgba(0, 128, 0, 1)">#</span><span style="color: rgba(0, 128, 0, 1)"> 守护线程</span>
<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)"> MyThreading(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, name):
      super(MyThreading, self).</span><span style="color: rgba(128, 0, 128, 1)">__init__</span><span style="color: rgba(0, 0, 0, 1)">()
      self.name </span>=<span style="color: rgba(0, 0, 0, 1)"> name

    </span><span style="color: rgba(0, 128, 0, 1)">#</span><span style="color: rgba(0, 128, 0, 1)"> 线程要运行的代码</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, 0, 255, 1)">print</span>(<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">我是线程%s</span><span style="color: rgba(128, 0, 0, 1)">"</span> %<span style="color: rgba(0, 0, 0, 1)"> self.name)
      time.sleep(</span>2<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运行结束</span><span style="color: rgba(128, 0, 0, 1)">"</span> %<span style="color: rgba(0, 0, 0, 1)"> self.name)


t1 </span>= MyThreading(1<span style="color: rgba(0, 0, 0, 1)">)
t2 </span>= MyThreading(2<span style="color: rgba(0, 0, 0, 1)">)
start_time </span>=<span style="color: rgba(0, 0, 0, 1)"> time.time()
t1.setDaemon(True)
t1.start()
t2.setDaemon(True)
t2.start()
end_time </span>=<span style="color: rgba(0, 0, 0, 1)"> time.time()
</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)">两个线程一共的运行时间为:</span><span style="color: rgba(128, 0, 0, 1)">"</span>, end_time-<span style="color: rgba(0, 0, 0, 1)">start_time)
</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)">主线程结束</span><span style="color: rgba(128, 0, 0, 1)">"</span>)</pre>
</div>
<span class="cnblogs_code_collapse">守护线程</span></div>
</li>
<li>注意:如果要设置为守护线程,一定要在开启线程之前,将该线程设置为守护线程</li>
<li>结论:主线程结束后,无论子线程1,2是否运行完成,都结束线程,不在继续向下运行</li>
</ul>
</li>
第三种:加入join方法设置同步
<ul>
<li>当不给程序设置守护进程时,主程序将一直等待子程序全部运行完成才结束</li>
<li>代码演示:
<div class="cnblogs_code"><img id="code_img_closed_380dfc2f-cf3f-4e08-8d92-94e7191e5503" class="code_img_closed lazyload" data-src="http://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif"><img id="code_img_opened_380dfc2f-cf3f-4e08-8d92-94e7191e5503" class="code_img_opened lazyload" style="display: none" data-src="http://images.cnblogs.com/OutliningIndicators/ExpandedBlockStart.gif">
<div id="cnblogs_code_open_380dfc2f-cf3f-4e08-8d92-94e7191e5503" class="cnblogs_code_hide">
<pre><span style="color: rgba(0, 128, 0, 1)">#</span><span style="color: rgba(0, 128, 0, 1)"> join:线程同步</span>
<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)"> MyThreading(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, name):
      super(MyThreading, self).</span><span style="color: rgba(128, 0, 128, 1)">__init__</span><span style="color: rgba(0, 0, 0, 1)">()
      self.name </span>=<span style="color: rgba(0, 0, 0, 1)"> name

    </span><span style="color: rgba(0, 128, 0, 1)">#</span><span style="color: rgba(0, 128, 0, 1)"> 线程要运行的代码</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, 0, 255, 1)">print</span>(<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">我是线程%s</span><span style="color: rgba(128, 0, 0, 1)">"</span> %<span style="color: rgba(0, 0, 0, 1)"> self.name)
      time.sleep(</span>3<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运行结束</span><span style="color: rgba(128, 0, 0, 1)">"</span> %<span style="color: rgba(0, 0, 0, 1)"> self.name)


threading_list </span>=<span style="color: rgba(0, 0, 0, 1)"> []
start_time </span>=<span style="color: rgba(0, 0, 0, 1)"> time.time()
</span><span style="color: rgba(0, 0, 255, 1)">for</span> x <span style="color: rgba(0, 0, 255, 1)">in</span> range(50<span style="color: rgba(0, 0, 0, 1)">):
    t </span>=<span style="color: rgba(0, 0, 0, 1)"> MyThreading(x)
    t.start()
    threading_list.append(t)

</span><span style="color: rgba(0, 0, 255, 1)">for</span> x <span style="color: rgba(0, 0, 255, 1)">in</span><span style="color: rgba(0, 0, 0, 1)"> threading_list:
    x.join()    </span><span style="color: rgba(0, 128, 0, 1)">#</span><span style="color: rgba(0, 128, 0, 1)"> 为线程开启同步</span>
<span style="color: rgba(0, 0, 0, 1)">
end_time </span>=<span style="color: rgba(0, 0, 0, 1)"> time.time()
</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)">50个线程一共的运行时间为:</span><span style="color: rgba(128, 0, 0, 1)">"</span>, end_time-<span style="color: rgba(0, 0, 0, 1)">start_time)
</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)">主线程结束</span><span style="color: rgba(128, 0, 0, 1)">"</span>)</pre>
</div>
<span class="cnblogs_code_collapse">join方法</span></div>
</li>
<li>结论:主线程等待50个子线程全部执行完成才结束。</li>
</ul>
</ul>
<h2><strong>线程锁(互斥锁Mutex)</strong></h2>
<ul>
<li>一个进程下可以启用多个线程,多个线程共享父进程的内存空间,也就意味着每个线程可以访问同一份数据,此时如果多个线程同时要修改一份数据,会出现什么状况?
<ul>
<li>代码演示:
<div class="cnblogs_code"><img id="code_img_closed_d08be986-6e36-4641-a6bb-f2e2bbbee6af" class="code_img_closed lazyload" data-src="http://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif"><img id="code_img_opened_d08be986-6e36-4641-a6bb-f2e2bbbee6af" class="code_img_opened lazyload" style="display: none" data-src="http://images.cnblogs.com/OutliningIndicators/ExpandedBlockStart.gif">
<div id="cnblogs_code_open_d08be986-6e36-4641-a6bb-f2e2bbbee6af" class="cnblogs_code_hide">
<pre><span style="color: rgba(0, 128, 0, 1)">#</span><span style="color: rgba(0, 128, 0, 1)"> -*- coding:utf8-*-</span>
<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

num </span>= 100<span style="color: rgba(0, 0, 0, 1)">
threading_list </span>=<span style="color: rgba(0, 0, 0, 1)"> []


</span><span style="color: rgba(0, 0, 255, 1)">def</span><span style="color: rgba(0, 0, 0, 1)"> fun():
    </span><span style="color: rgba(0, 0, 255, 1)">global</span><span style="color: rgba(0, 0, 0, 1)"> num
    </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)">get num:</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">, num)
    num </span>+= 1<span style="color: rgba(0, 0, 0, 1)">
    time.sleep(</span>1<span style="color: rgba(0, 0, 0, 1)">)


</span><span style="color: rgba(0, 0, 255, 1)">for</span> x <span style="color: rgba(0, 0, 255, 1)">in</span> range(200<span style="color: rgba(0, 0, 0, 1)">):
    t </span>= threading.Thread(target=<span style="color: rgba(0, 0, 0, 1)">fun)
    t.start()
    threading_list.append(t)

</span><span style="color: rgba(0, 0, 255, 1)">for</span> x <span style="color: rgba(0, 0, 255, 1)">in</span><span style="color: rgba(0, 0, 0, 1)"> threading_list:
    x.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)">nun:</span><span style="color: rgba(128, 0, 0, 1)">"</span>, num)</pre>
</div>
<span class="cnblogs_code_collapse">View Code</span></div>
</li>
<li>结论:运行结果可能会出现num&lt;300的情况</li>
<li>
<p>正常来讲,这个num结果应该是300, 但在python 2.7上多运行几次,会发现,最后打印出来的num结果不总是300,为什么每次运行的结果不一样呢? 哈,很简单,假设你有A,B两个线程,此时都 要对num 进行加1操作, 由于2个线程是并发同时运行的,所以2个线程很有可能同时拿走了num=100这个初始变量交给cpu去运算,当A线程去处完的结果是101,但此时B线程运算完的结果也是101,两个线程同时CPU运算的结果再赋值给num变量后,结果就都是101。那怎么办呢? 很简单,每个线程在要修改公共数据时,为了避免自己在还没改完的时候别人也来修改此数据,可以给这个数据加一把锁, 这样其它线程想修改此数据时就必须等待你修改完毕并把锁释放掉后才能再访问此数据。&nbsp;</p>
<p>*注:不要在3.x上运行,不知为什么,3.x上的结果总是正确的,可能是自动加了锁</p>
</li>
</ul>
</li>
<li>加锁版本:
<div class="cnblogs_code"><img id="code_img_closed_2980dd7d-406b-4215-b6e9-a0777e51383a" class="code_img_closed lazyload" data-src="http://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif"><img id="code_img_opened_2980dd7d-406b-4215-b6e9-a0777e51383a" class="code_img_opened lazyload" style="display: none" data-src="http://images.cnblogs.com/OutliningIndicators/ExpandedBlockStart.gif">
<div id="cnblogs_code_open_2980dd7d-406b-4215-b6e9-a0777e51383a" class="cnblogs_code_hide">
<pre><span style="color: rgba(0, 0, 255, 1)">import</span><span style="color: rgba(0, 0, 0, 1)"> random
</span><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
num </span>= 100<span style="color: rgba(0, 0, 0, 1)">
threading_list </span>=<span style="color: rgba(0, 0, 0, 1)"> []


</span><span style="color: rgba(0, 0, 255, 1)">def</span><span style="color: rgba(0, 0, 0, 1)"> fun():
    </span><span style="color: rgba(0, 0, 255, 1)">global</span><span style="color: rgba(0, 0, 0, 1)"> num
    time.sleep(random.random())
    lock.acquire() </span><span style="color: rgba(0, 128, 0, 1)">#</span><span style="color: rgba(0, 128, 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)">get num:</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">, num, threading.current_thread())
    num </span>+= 1<span style="color: rgba(0, 0, 0, 1)">
    lock.release()</span><span style="color: rgba(0, 128, 0, 1)">#</span><span style="color: rgba(0, 128, 0, 1)"> 释放锁</span>


<span style="color: rgba(0, 128, 0, 1)">#</span><span style="color: rgba(0, 128, 0, 1)"> 实例化锁对象</span>
lock =<span style="color: rgba(0, 0, 0, 1)"> threading.Lock()
</span><span style="color: rgba(0, 0, 255, 1)">for</span> x <span style="color: rgba(0, 0, 255, 1)">in</span> range(200<span style="color: rgba(0, 0, 0, 1)">):
    t </span>= threading.Thread(target=<span style="color: rgba(0, 0, 0, 1)">fun)
    t.start()
    threading_list.append(t)

</span><span style="color: rgba(0, 0, 255, 1)">for</span> x <span style="color: rgba(0, 0, 255, 1)">in</span><span style="color: rgba(0, 0, 0, 1)"> threading_list:
    x.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)">num:</span><span style="color: rgba(128, 0, 0, 1)">"</span>, num)</pre>
</div>
<span class="cnblogs_code_collapse">View Code</span></div>
</li>
</ul>
<p>&nbsp;</p>
<p><strong>GIL VS Lock&nbsp;</strong></p>
<p>机智的同学可能会问到这个问题,就是既然你之前说过了,Python已经有一个GIL来保证同一时间只能有一个线程来执行了,为什么这里还需要lock? 注意啦,这里的lock是用户级的lock,跟那个GIL没关系 ,具体我们通过下图来看一下+配合我现场讲给大家,就明白了。</p>
<p>&nbsp;</p>
<p><img alt="" width="965" height="542" loading="lazy" src="https://img2020.cnblogs.com/blog/1344326/202112/1344326-20211220232503219-720151912.png"></p>
<p>那你又问了, 既然用户程序已经自己有锁了,那为什么C python还需要GIL呢?加入GIL主要的原因是为了降低程序的开发的复杂度,比如现在的你写python不需要关心内存回收的问题,因为Python解释器帮你自动定期进行内存回收,你可以理解为python解释器里有一个独立的线程,每过一段时间它起wake up做一次全局轮询看看哪些内存数据是可以被清空的,此时你自己的程序 里的线程和 py解释器自己的线程是并发运行的,假设你的线程删除了一个变量,py解释器的垃圾回收线程在清空这个变量的过程中的clearing时刻,可能一个其它线程正好又重新给这个还没来及得清空的内存空间赋值了,结果就有可能新赋值的数据被删除了,为了解决类似的问题,python解释器简单粗暴的加了锁,即当一个线程运行时,其它人都不能动,这样就解决了上述的问题, &nbsp;这可以说是Python早期版本的遗留问题。</p>
<h2><strong>RLock(递归锁)</strong></h2>
<ul>
<li>说白了就是在一个大锁中还要再包含子锁</li>
</ul>
<div class="cnblogs_code"><img src="http://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif"><img id="code_img_opened_8376baa7-3ec1-48a3-986c-7c56b2cce705" class="code_img_opened lazyload" style="display: none" data-src="http://images.cnblogs.com/OutliningIndicators/ExpandedBlockStart.gif">
<div id="cnblogs_code_open_8376baa7-3ec1-48a3-986c-7c56b2cce705" class="cnblogs_code_hide">
<pre><span style="color: rgba(0, 0, 255, 1)">import</span><span style="color: rgba(0, 0, 0, 1)"> threading, time


</span><span style="color: rgba(0, 0, 255, 1)">def</span><span style="color: rgba(0, 0, 0, 1)"> run1():
    lock.acquire()
    </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)">grab the first part data</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)">global</span><span style="color: rgba(0, 0, 0, 1)"> num
    num </span>+= 1<span style="color: rgba(0, 0, 0, 1)">
    lock.release()
    </span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> num


</span><span style="color: rgba(0, 0, 255, 1)">def</span><span style="color: rgba(0, 0, 0, 1)"> run2():
    lock.acquire()
    </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)">grab the second part data</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)">global</span><span style="color: rgba(0, 0, 0, 1)"> num2
    num2 </span>+= 1<span style="color: rgba(0, 0, 0, 1)">
    lock.release()
    </span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> num2


</span><span style="color: rgba(0, 0, 255, 1)">def</span><span style="color: rgba(0, 0, 0, 1)"> run3():
    lock.acquire()
    res </span>=<span style="color: rgba(0, 0, 0, 1)"> run1()
    </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)">--------between run1 and run2-----</span><span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(0, 0, 0, 1)">)
    res2 </span>=<span style="color: rgba(0, 0, 0, 1)"> run2()
    lock.release()
    </span><span style="color: rgba(0, 0, 255, 1)">print</span><span style="color: rgba(0, 0, 0, 1)">(res, res2)


</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)">:
    num, num2 </span>=<span style="color: rgba(0, 0, 0, 1)"> 0, 0
    lock </span>=<span style="color: rgba(0, 0, 0, 1)"> threading.RLock()
    </span><span style="color: rgba(0, 0, 255, 1)">for</span> i <span style="color: rgba(0, 0, 255, 1)">in</span> range(3<span style="color: rgba(0, 0, 0, 1)">):
      t </span>= threading.Thread(target=<span style="color: rgba(0, 0, 0, 1)">run3)
      t.start()

</span><span style="color: rgba(0, 0, 255, 1)">while</span> threading.active_count() != 1<span style="color: rgba(0, 0, 0, 1)">:
    </span><span style="color: rgba(0, 0, 255, 1)">print</span><span style="color: rgba(0, 0, 0, 1)">(threading.active_count())
</span><span style="color: rgba(0, 0, 255, 1)">else</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)">----all threads done---</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>(num, num2)</pre>
</div>
<span class="cnblogs_code_collapse">View Code</span></div>
<h2><strong>在开发的过程中要注意有些操作默认都是 线程安全的(内部集成了锁的机制),我们在使用的时无需再通过锁再处理,例如:</strong></h2>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 255, 1)">import</span><span style="color: rgba(0, 0, 0, 1)"> threading

data_list </span>=<span style="color: rgba(0, 0, 0, 1)"> []

lock_object </span>=<span style="color: rgba(0, 0, 0, 1)"> threading.RLock()


</span><span style="color: rgba(0, 0, 255, 1)">def</span><span style="color: rgba(0, 0, 0, 1)"> task():
    </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)">开始</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)">for</span> i <span style="color: rgba(0, 0, 255, 1)">in</span> range(1000000<span style="color: rgba(0, 0, 0, 1)">):
      data_list.append(i)
    </span><span style="color: rgba(0, 0, 255, 1)">print</span><span style="color: rgba(0, 0, 0, 1)">(len(data_list))


</span><span style="color: rgba(0, 0, 255, 1)">for</span> i <span style="color: rgba(0, 0, 255, 1)">in</span> range(2<span style="color: rgba(0, 0, 0, 1)">):
    t </span>= threading.Thread(target=<span style="color: rgba(0, 0, 0, 1)">task)
    t.start()</span></pre>
</div>
<p><img alt="" width="1086" height="367" loading="lazy" src="https://img2022.cnblogs.com/blog/1344326/202202/1344326-20220207185200191-230898776.png"></p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<h2><strong>Semaphore(信号量)</strong></h2>
<ul>
<li>互斥锁同时只允许一个线程修改数据,而Semaphore是同时允许一定数量的线程修改数据,比如厕所有三个坑,那最多只允许三个人上厕所,后面的人只能等前面的人出来才能进去。</li>
<li>代码演示:
<div class="cnblogs_code"><img src="http://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif"><img id="code_img_opened_970e5b6e-fd7a-4832-a8bc-dcb770e864e8" class="code_img_opened lazyload" style="display: none" data-src="http://images.cnblogs.com/OutliningIndicators/ExpandedBlockStart.gif">
<div id="cnblogs_code_open_970e5b6e-fd7a-4832-a8bc-dcb770e864e8" class="cnblogs_code_hide">
<pre><span style="color: rgba(0, 128, 0, 1)">#</span><span style="color: rgba(0, 128, 0, 1)"> -*- coding:GBK -*-</span>
<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

sum_1 </span>=<span style="color: rgba(0, 0, 0, 1)"> 0
</span><span style="color: rgba(0, 0, 255, 1)">def</span><span style="color: rgba(0, 0, 0, 1)"> run(i):
    </span><span style="color: rgba(0, 0, 255, 1)">global</span><span style="color: rgba(0, 0, 0, 1)"> sum_1
    time.sleep(</span>1<span style="color: rgba(0, 0, 0, 1)">)
    </span><span style="color: rgba(0, 128, 0, 1)">#</span><span style="color: rgba(0, 128, 0, 1)"> lock.acquire()</span>
<span style="color: rgba(0, 0, 0, 1)">    semaphore.acquire()
    sum_1 </span>+= 1
    <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来了,并修改了sum_1的值为:%s</span><span style="color: rgba(128, 0, 0, 1)">"</span> %<span style="color: rgba(0, 0, 0, 1)"> (i, sum_1))
    semaphore.release()
    </span><span style="color: rgba(0, 128, 0, 1)">#</span><span style="color: rgba(0, 128, 0, 1)"> lock.release()</span>

<span style="color: rgba(0, 128, 0, 1)">#</span><span style="color: rgba(0, 128, 0, 1)"> lock = threading.Lock()</span>
semaphore = threading.BoundedSemaphore(5<span style="color: rgba(0, 0, 0, 1)">)

</span><span style="color: rgba(0, 0, 255, 1)">for</span> x <span style="color: rgba(0, 0, 255, 1)">in</span> range(10<span style="color: rgba(0, 0, 0, 1)">):
    t </span>= threading.Thread(target=run, args=<span style="color: rgba(0, 0, 0, 1)">(x,))
    t.start()

</span><span style="color: rgba(0, 0, 255, 1)">while</span> threading.active_count() != 1<span style="color: rgba(0, 0, 0, 1)">:
    </span><span style="color: rgba(0, 0, 255, 1)">pass</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)">程序结束</span><span style="color: rgba(128, 0, 0, 1)">"</span>)</pre>
</div>
<span class="cnblogs_code_collapse">View Code</span></div>
</li>
</ul>
<h2><strong>Event(事件)</strong></h2>
<ul>
<li><strong>通过Event来实现两个或多个线程间的交互,下面是一个红绿灯的例子,即起动一个线程做交通指挥灯,生成几个线程做车辆,车辆行驶按红灯停,绿灯行的规则。<br></strong></li>
<li>四个常用方法
<div class="cnblogs_code">
<pre>set()<span style="color: rgba(0, 128, 0, 1)">#</span><span style="color: rgba(0, 128, 0, 1)"> 设置标志位为 True</span>
clear()   <span style="color: rgba(0, 128, 0, 1)">#</span><span style="color: rgba(0, 128, 0, 1)"> 清空标志位(将标志位改为false)</span>
is_set()<span style="color: rgba(0, 128, 0, 1)">#</span><span style="color: rgba(0, 128, 0, 1)"> 检测标志位,如果标志位被设置,返回True,否则返回False</span>
wait()   <span style="color: rgba(0, 128, 0, 1)">#</span><span style="color: rgba(0, 128, 0, 1)"> 等待标志位被设置位True程序才继续往下运行</span></pre>
</div>
</li>
<li>代码演示:
<div class="cnblogs_code"><img id="code_img_closed_e355c195-f1b8-48ae-bd6f-341bbecf1aeb" class="code_img_closed lazyload" data-src="http://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif"><img id="code_img_opened_e355c195-f1b8-48ae-bd6f-341bbecf1aeb" class="code_img_opened lazyload" style="display: none" data-src="http://images.cnblogs.com/OutliningIndicators/ExpandedBlockStart.gif">
<div id="cnblogs_code_open_e355c195-f1b8-48ae-bd6f-341bbecf1aeb" class="cnblogs_code_hide">
<pre><span style="color: rgba(0, 128, 0, 1)">#</span><span style="color: rgba(0, 128, 0, 1)"> -*- coding:utf-8 -*-</span>
<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)"> light():
    count </span>= 1<span style="color: rgba(0, 0, 0, 1)">
    event.set()</span><span style="color: rgba(0, 128, 0, 1)">#</span><span style="color: rgba(0, 128, 0, 1)"> 设置标志位 True</span>
    <span style="color: rgba(0, 0, 255, 1)">while</span><span style="color: rgba(0, 0, 0, 1)"> True:
      </span><span style="color: rgba(0, 0, 255, 1)">if</span> count &lt;= 10<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)">现在是绿灯</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">)
            time.sleep(</span>1<span style="color: rgba(0, 0, 0, 1)">)
      </span><span style="color: rgba(0, 0, 255, 1)">elif</span> count &lt;= 15<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)">现在是红灯</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">)
            event.clear()   </span><span style="color: rgba(0, 128, 0, 1)">#</span><span style="color: rgba(0, 128, 0, 1)"> 清空标志位(将标志位改为false)</span>
            time.sleep(1<span style="color: rgba(0, 0, 0, 1)">)
      </span><span style="color: rgba(0, 0, 255, 1)">else</span><span style="color: rgba(0, 0, 0, 1)">:
            count </span>=<span style="color: rgba(0, 0, 0, 1)"> 0
            event.set()
      count </span>+= 1


<span style="color: rgba(0, 0, 255, 1)">def</span><span style="color: rgba(0, 0, 0, 1)"> car(name):
    </span><span style="color: rgba(0, 0, 255, 1)">while</span><span style="color: rgba(0, 0, 0, 1)"> True:
      </span><span style="color: rgba(0, 0, 255, 1)">if</span><span style="color: rgba(0, 0, 0, 1)"> event.is_set():
            </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在起飞-------------</span><span style="color: rgba(128, 0, 0, 1)">"</span> %<span style="color: rgba(0, 0, 0, 1)"> name)
            time.sleep(</span>1<span style="color: rgba(0, 0, 0, 1)">)
      </span><span style="color: rgba(0, 0, 255, 1)">else</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)">---------%s在等红灯---------------</span><span style="color: rgba(128, 0, 0, 1)">"</span> %<span style="color: rgba(0, 0, 0, 1)"> name)
            event.wait()   </span><span style="color: rgba(0, 128, 0, 1)">#</span><span style="color: rgba(0, 128, 0, 1)"> 等待标志位被设置位True程序才继续往下运行</span>
<span style="color: rgba(0, 0, 0, 1)">

event </span>=<span style="color: rgba(0, 0, 0, 1)"> threading.Event()
light_1 </span>= threading.Thread(target=<span style="color: rgba(0, 0, 0, 1)">light)
light_1.start()
</span><span style="color: rgba(0, 0, 255, 1)">for</span> x <span style="color: rgba(0, 0, 255, 1)">in</span> range(5<span style="color: rgba(0, 0, 0, 1)">):
    car_1 </span>= threading.Thread(target=car, args=(<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)">"</span>+<span style="color: rgba(0, 0, 0, 1)">str(x),))
    car_1.start()</span></pre>
</div>
<span class="cnblogs_code_collapse">红绿灯案例</span></div>
<p>&nbsp;</p>
</li>
</ul>
<h2><strong>Queue(队列)</strong></h2>
<div class="cnblogs_code">
<pre>queue.Queue(maxsize=<span style="color: rgba(0, 0, 0, 1)">0)
</span><span style="color: rgba(0, 128, 0, 1)">#</span><span style="color: rgba(0, 128, 0, 1)">队列:先进先出maxsize:设置队列的大小</span>
queue.LifoQueue(maxsize=<span style="color: rgba(0, 0, 0, 1)">0)
</span><span style="color: rgba(0, 128, 0, 1)">#</span><span style="color: rgba(0, 128, 0, 1)">#last in fisrt outmaxsize:设置队列的大小</span>
queue.PriorityQueue(maxsize=<span style="color: rgba(0, 0, 0, 1)">0)
</span><span style="color: rgba(0, 128, 0, 1)">#</span><span style="color: rgba(0, 128, 0, 1)">存储数据时可设置优先级的队列,按优先级顺序(最低的先)maxsize:设置队列的大小</span></pre>
</div>
<p><strong><em class="property">exception&nbsp;</em><code class="descclassname">queue.</code><code class="descname">Empty</code></strong></p>
<p>Exception raised when non-blocking&nbsp;<code class="xref py py-meth docutils literal"><span class="pre">get()</span></code>&nbsp;(or&nbsp;<code class="xref py py-meth docutils literal"><span class="pre">get_nowait()</span></code>) is called on a&nbsp;<code class="xref py py-class docutils literal"><span class="pre">Queue</span></code>&nbsp;object which is empty.</p>
<p>当在一个空的队列对象上调用非阻塞的get()(或get_nowait())时,会产生异常。</p>
<p>&nbsp;</p>
<p><strong><em class="property">exception&nbsp;</em><code class="descclassname">queue.</code><code class="descname">Full</code></strong></p>
<p>Exception raised when non-blocking&nbsp;<code class="xref py py-meth docutils literal"><span class="pre">put()</span></code>&nbsp;(or&nbsp;<code class="xref py py-meth docutils literal"><span class="pre">put_nowait()</span></code>) is called on a&nbsp;<code class="xref py py-class docutils literal"><span class="pre">Queue</span></code>&nbsp;object which is full.</p>
<p>当非阻塞的put()(或put_nowait())被调用到一个已满的队列对象上时引发的异常。</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 255, 1)">import</span><span style="color: rgba(0, 0, 0, 1)"> queue
</span><span style="color: rgba(0, 128, 0, 1)">#</span><span style="color: rgba(0, 128, 0, 1)"> 实例化队列对象</span>
q = queue.Queue(3<span style="color: rgba(0, 0, 0, 1)">)
</span><span style="color: rgba(0, 0, 255, 1)">print</span>(q.qsize())    <span style="color: rgba(0, 128, 0, 1)">#</span><span style="color: rgba(0, 128, 0, 1)"> 获取队列内数据的长度</span>
<span style="color: rgba(0, 0, 255, 1)">print</span>(q.empty())    <span style="color: rgba(0, 128, 0, 1)">#</span><span style="color: rgba(0, 128, 0, 1)"> 如果队列是空的,返回True,否则返回False(不可靠!)</span>
<span style="color: rgba(0, 0, 255, 1)">print</span>(q.full())   <span style="color: rgba(0, 128, 0, 1)">#</span><span style="color: rgba(0, 128, 0, 1)"> 如果队列已满,返回True,否则返回False(不可靠!)。</span>
<span style="color: rgba(128, 0, 0, 1)">"""</span><span style="color: rgba(128, 0, 0, 1)">
Queue.put(item, block=True, timeout=None)
可以简写:Queue.put(item, True, 5)
将项目放入队列。
如果可选的args block为true(默认值),并且timeout为None(默认值),必要时进行阻塞,直到有空闲的槽。
如果timeout是一个正数,它最多阻断timeout秒,如果在这段时间内没有空闲槽,则引发Full异常。
否则(block为false),如果有空闲的槽立即可用,就在队列上放一个项目,否则就引发Full异常(在这种情况下忽略超时)。
</span><span style="color: rgba(128, 0, 0, 1)">"""</span><span style="color: rgba(0, 0, 0, 1)">
q.put(</span>1)            <span style="color: rgba(0, 128, 0, 1)">#</span><span style="color: rgba(0, 128, 0, 1)"> 数据“1”进入队列</span>
q.put(<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">nihao</span><span style="color: rgba(128, 0, 0, 1)">"</span>)      <span style="color: rgba(0, 128, 0, 1)">#</span><span style="color: rgba(0, 128, 0, 1)"> 数据"nihao"进入队列</span>
q.put(<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">456ni</span><span style="color: rgba(128, 0, 0, 1)">"</span>, block=True, timeout=5<span style="color: rgba(0, 0, 0, 1)">)
</span><span style="color: rgba(128, 0, 0, 1)">'''</span><span style="color: rgba(128, 0, 0, 1)">
将一个项目放入队列中,不进行阻断。
只有在有空闲位置的情况下才排队。
否则会引发Full异常。
</span><span style="color: rgba(128, 0, 0, 1)">'''</span>
<span style="color: rgba(0, 128, 0, 1)">#</span><span style="color: rgba(0, 128, 0, 1)"> q.put_nowait(123)</span>

<span style="color: rgba(128, 0, 0, 1)">'''</span><span style="color: rgba(128, 0, 0, 1)">
Queue.get(block=True, timeout=None)
可以简写:Queue.get(True, 3)
从队列中删除并返回一个项目。
如果可选的args'block'为True(默认),'timeout'为无(默认)。
    就会在必要时阻塞,直到有一个项目可用。
    如果'timeout'是非负数,它最多阻断'timeout'秒,如果在这段时间内没有项目可用,则引发Empty异常。
否则('block'为False),如果有一个项目立即可用,则返回一个项目。
    否则引发Empty异常('timeout'被忽略了在这种情况下)。
</span><span style="color: rgba(128, 0, 0, 1)">'''</span>
<span style="color: rgba(0, 0, 255, 1)">print</span><span style="color: rgba(0, 0, 0, 1)">(q.get())
</span><span style="color: rgba(0, 0, 255, 1)">print</span><span style="color: rgba(0, 0, 0, 1)">(q.get())
</span><span style="color: rgba(0, 0, 255, 1)">print</span><span style="color: rgba(0, 0, 0, 1)">(q.get())
</span><span style="color: rgba(0, 0, 255, 1)">print</span>(q.get(block=True, timeout=2<span style="color: rgba(0, 0, 0, 1)">))
</span><span style="color: rgba(128, 0, 0, 1)">'''</span><span style="color: rgba(128, 0, 0, 1)">
从队列中移除并返回一个项目,而不阻塞。
只有当一个项目立即可用时,才会得到一个项目。
否则引发Empty异常。
</span><span style="color: rgba(128, 0, 0, 1)">'''</span>
<span style="color: rgba(0, 128, 0, 1)">#</span><span style="color: rgba(0, 128, 0, 1)"> print(q.get_nowait())</span></pre>
</div>
<p>&nbsp;</p>
<h2><span class="sig-paren">生产者消费者模型</span></h2>
<p>在并发编程中使用生产者和消费者模式能够解决绝大多数并发问题。该模式通过平衡生产线程和消费线程的工作能力来提高程序的整体处理数据的速度。</p>
<p><strong>为什么要使用生产者和消费者模式</strong></p>
<p>在线程世界里,生产者就是生产数据的线程,消费者就是消费数据的线程。在多线程开发当中,如果生产者处理速度很快,而消费者处理速度很慢,那么生产者就必须等待消费者处理完,才能继续生产数据。同样的道理,如果消费者的处理能力大于生产者,那么消费者就必须等待生产者。为了解决这个问题于是引入了生产者和消费者模式。</p>
<p><strong>什么是生产者消费者模式</strong></p>
<p>生产者消费者模式是通过一个容器来解决生产者和消费者的强耦合问题。生产者和消费者彼此之间不直接通讯,而通过阻塞队列来进行通讯,所以生产者生产完数据之后不用等待消费者处理,直接扔给阻塞队列,消费者不找生产者要数据,而是直接从阻塞队列里取,阻塞队列就相当于一个缓冲区,平衡了生产者和消费者的处理能力。</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 128, 0, 1)">#</span><span style="color: rgba(0, 128, 0, 1)"> 生产者/消费者</span>
<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)"> queue
</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, 128, 0, 1)">#</span><span style="color: rgba(0, 128, 0, 1)"> 生产者</span>
<span style="color: rgba(0, 0, 255, 1)">def</span><span style="color: rgba(0, 0, 0, 1)"> producer(name):
    count </span>= 1
    <span style="color: rgba(0, 0, 255, 1)">while</span><span style="color: rgba(0, 0, 0, 1)"> True:
      p.put(</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)">"</span><span style="color: rgba(0, 0, 0, 1)">.format(name, count))
      </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)">骨头{}被{}生产</span><span style="color: rgba(128, 0, 0, 1)">"</span>.format(count, name).center(60, <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)">"</span><span style="color: rgba(0, 0, 0, 1)">))
      count </span>+= 1<span style="color: rgba(0, 0, 0, 1)">
      time.sleep(</span>0.1<span style="color: rgba(0, 0, 0, 1)">)


</span><span style="color: rgba(0, 128, 0, 1)">#</span><span style="color: rgba(0, 128, 0, 1)"> 消费者</span>
<span style="color: rgba(0, 0, 255, 1)">def</span><span style="color: rgba(0, 0, 0, 1)"> consumer(name):
    </span><span style="color: rgba(0, 0, 255, 1)">while</span><span style="color: rgba(0, 0, 0, 1)"> True:
      </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)">{}被{}吃掉了</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">.format(p.get(), name))


</span><span style="color: rgba(0, 128, 0, 1)">#</span><span style="color: rgba(0, 128, 0, 1)"> 实例化队列对象</span>
p = queue.Queue(10<span style="color: rgba(0, 0, 0, 1)">)
</span><span style="color: rgba(0, 128, 0, 1)">#</span><span style="color: rgba(0, 128, 0, 1)"> 创建生产者线程</span>
producer_threading1 = threading.Thread(target=producer, args=(<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)">"</span><span style="color: rgba(0, 0, 0, 1)">,))
producer_threading2 </span>= threading.Thread(target=producer, args=(<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">Alex</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">,))
</span><span style="color: rgba(0, 128, 0, 1)">#</span><span style="color: rgba(0, 128, 0, 1)"> 创建消费者线程</span>
consumer_threading1 = threading.Thread(target=consumer, args=(<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)">"</span><span style="color: rgba(0, 0, 0, 1)">,))
consumer_threading2 </span>= threading.Thread(target=consumer, args=(<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)">"</span><span style="color: rgba(0, 0, 0, 1)">,))
producer_threading1.start()
producer_threading2.start()
consumer_threading1.start()
consumer_threading2.start()</span></pre>
</div>
<p>&nbsp;</p>
<h2 class="md-end-block md-heading md-focus"><span class="md-plain md-expand">线程池</span></h2>
<p><span class="md-plain md-expand">Python3中官方才正式提供线程池。</span></p>
<p><span class="md-plain md-expand">线程不是开的越多越好,开的多了可能会导致系统的性能更低了,例如:如下的代码是不推荐在项目开发中编写。</span></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)">def</span><span style="color: rgba(0, 0, 0, 1)"> task(video_url):
    </span><span style="color: rgba(0, 0, 255, 1)">pass</span><span style="color: rgba(0, 0, 0, 1)">

url_list </span>= [<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">www.xxxx-{}.com</span><span style="color: rgba(128, 0, 0, 1)">"</span>.format(i) <span style="color: rgba(0, 0, 255, 1)">for</span> i <span style="color: rgba(0, 0, 255, 1)">in</span> range(30000<span style="color: rgba(0, 0, 0, 1)">)]

</span><span style="color: rgba(0, 0, 255, 1)">for</span> url <span style="color: rgba(0, 0, 255, 1)">in</span><span style="color: rgba(0, 0, 0, 1)"> url_list:
    t </span>= threading.Thread(target=task, args=<span style="color: rgba(0, 0, 0, 1)">(url,))
    t.start()

</span><span style="color: rgba(0, 128, 0, 1)">#</span><span style="color: rgba(0, 128, 0, 1)"> 这种每次都创建一个线程去操作,创建任务的太多,线程就会特别多,可能效率反倒降低了。</span></pre>
</div>
<p><span class="md-pair-s md-expand"><strong>建议</strong><span class="md-plain md-expand">:使用线程池</span></span></p>
<div class="cnblogs_code">
<pre><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)">from</span> concurrent.futures <span style="color: rgba(0, 0, 255, 1)">import</span> ThreadPoolExecutor<span style="color: rgba(0, 128, 0, 1)">#</span><span style="color: rgba(0, 128, 0, 1)"> 并行期货,线程池执行者</span>
<span style="color: rgba(128, 0, 0, 1)">"""</span><span style="color: rgba(128, 0, 0, 1)">
pool = ThreadPoolExecutor(100)
pool.submit(函数名,参数1,参数2,参数...)
</span><span style="color: rgba(128, 0, 0, 1)">"""</span>


<span style="color: rgba(0, 0, 255, 1)">def</span><span style="color: rgba(0, 0, 0, 1)"> task(video_url, num):
    </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)">开始执行任务</span><span style="color: rgba(128, 0, 0, 1)">"</span>, video_url, num)   <span style="color: rgba(0, 128, 0, 1)">#</span><span style="color: rgba(0, 128, 0, 1)"> 开始执行任务 www.xxxx-299.com 3</span>
    time.sleep(1<span style="color: rgba(0, 0, 0, 1)">)


</span><span style="color: rgba(0, 128, 0, 1)">#</span><span style="color: rgba(0, 128, 0, 1)"> 创建线程池,最多维护10个线程</span>
threadpool = ThreadPoolExecutor(10<span style="color: rgba(0, 0, 0, 1)">)
</span><span style="color: rgba(0, 128, 0, 1)">#</span><span style="color: rgba(0, 128, 0, 1)"> 生成300网址,并放入列表</span>
url_list = [<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">www.xxxx-{}.com</span><span style="color: rgba(128, 0, 0, 1)">"</span>.format(i) <span style="color: rgba(0, 0, 255, 1)">for</span> i <span style="color: rgba(0, 0, 255, 1)">in</span> range(300<span style="color: rgba(0, 0, 0, 1)">)]
</span><span style="color: rgba(0, 0, 255, 1)">for</span> url <span style="color: rgba(0, 0, 255, 1)">in</span><span style="color: rgba(0, 0, 0, 1)"> url_list:
    </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)">"""</span><span style="color: rgba(0, 0, 0, 1)">
    threadpool.submit(task, url, </span>3<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)">等待线程池中的任务执行完毕中······</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">)
threadpool.shutdown(True)   </span><span style="color: rgba(0, 128, 0, 1)">#</span><span style="color: rgba(0, 128, 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)">END</span><span style="color: rgba(128, 0, 0, 1)">"</span>)</pre>
</div>
<p><img alt="" width="322" height="164" loading="lazy" src="https://img2022.cnblogs.com/blog/1344326/202202/1344326-20220207191535609-59589383.png">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;<img src="https://img2022.cnblogs.com/blog/1344326/202202/1344326-20220207191645709-293922806.png"></p>
<p>任务执行完任务,再干点其他事:</p>
<div class="cnblogs_code">
<pre><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)">"""</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)">import</span><span style="color: rgba(0, 0, 0, 1)"> random
</span><span style="color: rgba(0, 0, 255, 1)">from</span> concurrent.futures <span style="color: rgba(0, 0, 255, 1)">import</span><span style="color: rgba(0, 0, 0, 1)"> ThreadPoolExecutor


</span><span style="color: rgba(0, 0, 255, 1)">def</span><span style="color: rgba(0, 0, 0, 1)"> task(video_url):
    </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)">开始执行任务</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">, video_url)
    time.sleep(</span>1<span style="color: rgba(0, 0, 0, 1)">)
    </span><span style="color: rgba(0, 0, 255, 1)">return</span> random.randint(0, 10)    <span style="color: rgba(0, 128, 0, 1)">#</span><span style="color: rgba(0, 128, 0, 1)"> 将结果封装成一个Futuer对象,返回给线程池</span>


<span style="color: rgba(0, 0, 255, 1)">def</span> done(response):   <span style="color: rgba(0, 128, 0, 1)">#</span><span style="color: rgba(0, 128, 0, 1)"> response就是futuer对象,也就是task的返回值分装的一个Futuer对象</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)">任务执行完后,回调的函数</span><span style="color: rgba(128, 0, 0, 1)">"</span>, response.result())    <span style="color: rgba(0, 128, 0, 1)">#</span><span style="color: rgba(0, 128, 0, 1)"> 即Futuer.result():取出task的返回值</span>


<span style="color: rgba(0, 128, 0, 1)">#</span><span style="color: rgba(0, 128, 0, 1)"> 创建线程池</span>
threadpool = ThreadPoolExecutor(10<span style="color: rgba(0, 0, 0, 1)">)
url_list </span>= [<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">www.xxxx-{}.com</span><span style="color: rgba(128, 0, 0, 1)">"</span>.format(i) <span style="color: rgba(0, 0, 255, 1)">for</span> i <span style="color: rgba(0, 0, 255, 1)">in</span> range(5<span style="color: rgba(0, 0, 0, 1)">)]
</span><span style="color: rgba(0, 0, 255, 1)">for</span> url <span style="color: rgba(0, 0, 255, 1)">in</span><span style="color: rgba(0, 0, 0, 1)"> url_list:
    futuer </span>= threadpool.submit(task, url)    <span style="color: rgba(0, 128, 0, 1)">#</span><span style="color: rgba(0, 128, 0, 1)"> futuer是由task返回的一个Future对象,里面有记录task的返回值</span>
    futuer.add_done_callback(done)         <span style="color: rgba(0, 128, 0, 1)">#</span><span style="color: rgba(0, 128, 0, 1)"> 回调done函数,执行者依然是子线程</span>

<span style="color: rgba(0, 128, 0, 1)">#</span><span style="color: rgba(0, 128, 0, 1)"> 优点:可以做分工,例如:task专门下载,done专门将下载的数据写入本地文件。</span></pre>
</div>
<p>&nbsp;</p><br><br>
来源:https://www.cnblogs.com/fjfsu/p/15709155.html
頁: [1]
查看完整版本: Python中的多线程