1. 线程概述

  在一个进程的内部,要同时干多件事情,就需要同时运行“多个子任务”,我们把进程内的这些“子任务”叫做线程。也就说线程是进程成的子任务。
  线程通常叫做情景的进程。线程是通过向内侧控件的并发执行的多任务。每一个线程都共享一个进程的资源。

  线程是最小的执行单元,而进程至少由一个线程组成。如何调度进程和线程,完全由操作系统决定,用户程序不能自己决定什么时候执行。执行多长时间。

  线程有两个模块:

    _thread模块:低级模块(现在很少用了)

    threading模块:高级模块,是对_thread的进一步封装。

2. 启动一个线程

  先上代码:

import threading,time

a =
def run(num):
print("子线程(%s)开始" % threading.current_thread().name) # 实现线程的功能
print("打印",num)
time.sleep()
print(a) print("子线程(%s)结束" % threading.current_thread().name) if __name__ == '__main__':
# 任何进程默认就会启动一个线程,成为主线程,主线程可以启动新的子程序
# current_thread().name 返回当前线程的实例的名称
print("主程序(%s)启动" % (threading.current_thread().name)) # 创建子进程
t = threading.Thread(target=run,name="runThread",args=(,))
t.start()
# 等待线程结束
t.join() print("主程序(%s)结束" % (threading.current_thread().name)) 主程序(MainThread)启动
子线程(runThread)开始
打印 子线程(runThread)结束
主程序(MainThread)结束

  说明1:任何进程默认就会启动一个线程,成为主线程,主线程可以启动新的子程序。

  说明2:current_thread().name方法是返回一个档期线程的实例名称。

3. 线程间的数据共享存在的问题

  多线程和多进程最大的不同在于:多进程中,同一个变量,格子有一份拷贝存在在每个进程中,互不影响,而多线性中,所有变量都由所有线程共享,所以任何一个变量都可以被任意一个线程修改,因此线程间的通向数据存在的最大危险在于多个线程同时修改一个变量,容易把内存内容改乱了。  

  实例1:我们以加一个减一个的方式不停循环一个很大的数,我们发现,数越大到最后就出现了混乱,而不是还是等于0的状态。

import threading

num = 

def run(n):
global num
for i in range():
num = num + n
num = num - n if __name__ == '__main__':
t1 = threading.Thread(target=run,args=(,))
t2 = threading.Thread(target=run,args=(,))
t1.start()
t2.start()
t1.join()
t1.join() print("num = ", str(num))
# num = -

  说明1:这种问题的存在,与进程不一样,而且为了达到相互的交替执行,而不出现随机性的混乱情况。我们加入了“锁”的概念。

4. 线程锁解决数据混乱

  在两个线程同时工作的时候,我们为了防止数据可能出现的混乱,加入了线程锁。

  先上代码:

import threading

# 创建锁对象
lock = threading.Lock() num = def run(n):
global num
for i in range():
# 锁
# 确保了这段代码由一个线程从头到尾的完成执行
# 阻止了多线程的并发执行,包含锁的某段代码,实际上只能以单线程模式执行,所以效率大大的降低了。
# 由于可以存在多个锁,不同线程可以持有不同的锁,并试图获取其他的锁,这样锁来锁去的,可能造成死锁,导致多个线程挂起。只能靠操作系统强制终止。
lock.acquire()
try:
num = num + n
num = num - n
finally:
# 开锁
lock.release() if __name__ == '__main__':
t1 = threading.Thread(target=run,args=(,))
t2 = threading.Thread(target=run,args=(,))
t1.start()
t2.start()
t1.join()
t1.join() print("num = ", str(num)) # num =
# 上锁后成功。

  说明:上锁我们用的方法是:lock.acquire()需要一把锁,解锁我们用的方法是:lock.release()释放这把锁。

  说明2:这样确保了这段代码由一个线程从头到尾的完成执行。阻止了多线程并发(前面提到过)的执行,包含锁的代码,实际上只能单线程执行,所以效率大大降低了。

  说明3:由于可以存在多个锁,不能线程可以持有不同的锁,并试图获取其他的锁,这样锁来锁去,可能造成死锁,导致多个线程挂起。只能靠系统强制终止。

  说明4:因此我们采用try...finally异常的代码处理。这样写稍微麻烦一些,之前我们在学文件的io操作的时候,用过一个with的方式,用这种方式文件也不需要再关闭。因此我们用另外一种写法,让锁也不用关闭了,他会自动去关闭。

  代码:

# 锁的第二种写法:
import threading # 创建锁对象
lock = threading.Lock() num = def run(n):
global num
for i in range():
# 与上面代码功能相同,with lock可以自动上锁与解锁。
with lock:
num = num + n
num = num - n if __name__ == '__main__':
t1 = threading.Thread(target=run,args=(,))
t2 = threading.Thread(target=run,args=(,))
t1.start()
t2.start()
t1.join()
t1.join() print("num = ", str(num)) # num =
# 上锁后成功。

5. threadLocal

  我们发现通过上锁开锁的这样方式容易造成程序错误导致死机的情况。我们可以采取用本地分配线程变量的方式,让这些变量的计算有自己的独立运行线程也可以实现这样的功能。因此我们把下面代码中的num变量交送给“local”的线程变量,让local在每个线程中独立去交互数据。

  代码:

import threading

num =
# 创建一个全局的ThreadLocal对象
# 每个线程有的独立存储空间
# 每个线程对ThreadLocal对象,而且互不影响。
local = threading.local() def run(x,n):
x = x + n
x = x - n def func(n):
# 每个线程都有local.x,就是线程的局部变量
local.x = num
for i in range():
run(local.x,n)
print("%s--%d" %(threading.current_thread().name,local.x)) if __name__ == '__main__':
t1 = threading.Thread(target=func,args=(,))
t2 = threading.Thread(target=func,args=(,))
t1.start()
t2.start()
t1.join()
t1.join() # Thread - - -
# Thread - - -
# 所用:为每个线程绑定一个数据库连接,或者是HTTP请求,或者是用户身份信息等,这样一个线程的所有调用到的处理函数都可以非常方便的访问这些资源。

 

6. 信号量控制线程数量(Semaphore)

  前面我们发现了,我们手工的去制作一个线程,但是我们其实不知道当前有多少个线程,这些线程的多少我们可以叫做信号量,我们可以通过信号量的多少来控制线程。

  代码:

import threading,time

# 控制线程的数量,让多少个线程一起执行
sem = threading.Semaphore() def run():
with sem:
for i in range():
print("%s - %d" %(threading.current_thread().name,i))
time.sleep()
if __name__ == '__main__':
for i in range():
threading.Thread(target=run).start() Thread- -
Thread- -
Thread- -
Thread- -
Thread- -
Thread- -
Thread- - 2Thread- - Thread- -
Thread- -
Thread- -
Thread- -
Thread- -
Thread- -
Thread- -
Thread- - 0Thread- - Thread- -
Thread- -
Thread- - 2Thread- - Thread- -
Thread- -
Thread- - 4Thread- -

  说明1:控制线程的数量,让多少个线程一起执行。

7. 凑够一定数量才能一起执行(Barrier):

  代码:

import threading,time

# 凑够多少个线程一起执
bar = threading.Barrier() def run():
print("%s - start" %(threading.current_thread().name))
time.sleep()
bar.wait()
print("%s - end" % (threading.current_thread().name))
# 如果凑不够一定数量不会执行,一直在那里等着 if __name__ == '__main__':
for i in range():
threading.Thread(target=run).start()

  说明1:我们发现程序一直停在那儿等待凑够一定的线程数量才能一起执行。其实这种方式在多数去情况下是用不到的。

8. 定时线程(Timer)

  我们之前又给线程上锁,又分配线程量的情况,我们如果想给线程定时去开启,需要用到Timer的方法。

  代码:

import threading

def run():
print("Thomas is a good man") # 延时执行线程
t = threading.Timer(,run) t.start()
t.join() print("父线程结束")
# Thomas is a good man
# 父线程结束

  说明1:这样我们等待了5秒钟,线程才开始执行程序。

9. 线程通信(event)

  前面我们使用手工的方式进行进行线程之间上锁解锁之间的方式进行性通讯的。我们可以调用线程的事件(因为线程行动本身就是一个事件),让上一个线程等待时间触发。

  代码:

import threading,time

def func():
# 事件对象
event = threading.Event()
def run():
for i in range():
# 阻塞,等待时间的触发
event.wait()
# clear是重置的意思
event.clear()
print("Thomas is a good man!!%d",i) threading.Thread(target=run).start()
return event e = func()
# 触发时间
for i in range():
e.set()
time.sleep()

10. 线程间的通信(Queue队列方式)

  代码:

import threading,queue,time,random

# 生产者
def producer(id,q):
while True:
num = random.randint(,)
q.put(num)
print("生产者%d生产了%d的数据放入了队列" %(id,num))
time.sleep()
# 任务完成
q.task_done() # 消费者
def customer(id,q):
while True:
item = q.get()
if item is None:
break
print("消费者%d消费了%d数据" %(id,item))
time.sleep()
# 任务完成
q.task_done() if __name__ == '__main__':
# 消息队列
q = queue.Queue() # 启动生产者
for i in range():
threading.Thread(target=producer,args=(i,q)).start() # 启动消费者
for i in range():
threading.Thread(target=customer,args=(i,q)).start()

11. 线程的调度(Condition方法)

  前面用事件去控制线程的开关,我们还可以通过线程的条件来控制。

  代码:

import threading,time

# 线程条件变量
cond = threading.Condition() def run1():
with cond:
for i in range(,,):
print(threading.current_thread().name,i)
time.sleep()
cond.wait()
cond.notify() def run2():
with cond:
for i in range(,,):
print(threading.current_thread().name,i)
time.sleep()
cond.notify()
cond.wait() threading.Thread(target=run1).start()
threading.Thread(target=run2).start()

  说明:这里有一点注意的是cond.wait控制等待,然后要通知下一个cond.notify这样才可以。

Python笔记_第四篇_高阶编程_进程、线程、协程_2.线程的更多相关文章

  1. Python笔记_第四篇_高阶编程_进程、线程、协程_4.协程

    1.协程的概念: 子程序或者子函数,在所有语言中都是层级调用,比如A调用B,再B执行的过程中又可以调用C,C执行完毕返回,B执行返回,最后是A执行完毕返回.是通过栈来实现的,一个线程就是执行一个自称, ...

  2. Python笔记_第四篇_高阶编程_进程、线程、协程_5.GPU加速

    Numba:高性能计算的高生产率 在这篇文章中,笔者将向你介绍一个来自Anaconda的Python编译器Numba,它可以在CUDA-capable GPU或多核cpu上编译Python代码.Pyt ...

  3. Python笔记_第四篇_高阶编程_进程、线程、协程_1.进程

    1. 多任务原理: 现代操作系统,像win,max os x,linux,unix等都支持多任务. * 什么叫做多任务? 操作系统可以同时运行多个任务. * 单核CPU实现多任务原理? 操作系统轮流让 ...

  4. Python开发【第十三篇】高阶函数、递归函数、闭包

    函数式编程是指用一系列函数解决问题 好处:用每个函数完成每个细小的功能,一系列函数任意组合能够解决大问题 函数仅仅接收输入并产生输出,不包含任何能影响输出的内部状态 函数之间的可重入性 当一个函数的输 ...

  5. python学习三十四天函数高阶函数定义及用法

    python函数高阶函数是把函数当成一个变量,传递给函数作为参数,或者函数的返回值里面有函数,都称为高阶函数, 1,把函数作为参数传递 def dac(x,y): return x+y def tes ...

  6. 多任务-python实现-进程,协程,线程总结(2.1.16)

    @ 目录 1.类比 2.总结 关于作者 1.类比 一个生产玩具的工厂: 一个生产线成为一个进程,一个生产线有多个工人,所以工人为线程 单进程-多线程:一条生产线,多个工人 多进程-多线程:多条生产线, ...

  7. Python笔记_第四篇_高阶编程_实例化方法、静态方法、类方法和属性方法概念的解析。

    1.先叙述静态方法: 我们知道Python调用类的方法的时候都要进行一个实例化的处理.在面向对象中,一把存在静态类,静态方法,动态类.动态方法等乱七八糟的这么一些叫法.其实这些东西看起来抽象,但是很好 ...

  8. Python笔记_第四篇_高阶编程_魔法(术)方法详解(重载的再详解)

    1. 魔法方法是什么? 魔法方法(Magic Method)是Python比较独特的应用,它可以给你的类增加特殊的方法,如果你的对象实现了(重载),这些方法中的某一个,就会被Python所调用.正如装 ...

  9. Python笔记_第四篇_高阶编程_再议装饰器和再议内置函数

    1. 概述: 我们在前面用了很多的装饰器这个工具的方法.这个位置要系统的讲一下装饰器. 1.2 为什么需要装饰器. 装饰器本质是一个Python函数,它可以让其他函数在不需要任何代码变动的前提下增加额 ...

  10. Python笔记_第四篇_高阶编程_正则表达式_2.正则表达式入门

    1. 匹配单个字符和数字: . --->> 匹配除换行符以外的任意字符.[0123456789] --->> []字符集合,表示匹配方括号中所包含的任意一个字符.[Thomas ...

随机推荐

  1. 指令——mv

    一个完整的指令的标准格式: Linux通用的格式——#指令主体(空格) [选项](空格) [操作对象] 一个指令可以包含多个选项,操作对象也可以是多个. 指令:mv   (move,移动,剪切) 作用 ...

  2. MySQL每日执行

    drop event if exists upload_deadline; DELIMITER $$ create event upload_deadline day starts timestamp ...

  3. Oracle之SQL优化专题03-如何看懂SQL的执行计划

    专题第一篇<Oracle之SQL优化专题01-查看SQL执行计划的方法>讲到了查看SQL执行计划的方法,并介绍了各种方法的应用场景,那么这一篇就主要介绍下如何看懂SQL的执行计划.毕竟如果 ...

  4. HDU_4965 Fast Matrix Calculation 2014多校9 矩阵快速幂+机智的矩阵结合律

    一开始看这个题目以为是个裸的矩阵快速幂的题目, 后来发现会超时,超就超在  M = C^(N*N). 这个操作,而C本身是个N*N的矩阵,N最大为1000. 但是这里有个巧妙的地方就是 C的来源其实 ...

  5. ACM-Satellite Photographs

    题目描述:Satellite Photographs Farmer John purchased satellite photos of W x H pixels of his farm (1 < ...

  6. 【剑指Offer】面试题04. 二维数组中的查找

    题目 在一个 n * m 的二维数组中,每一行都按照从左到右递增的顺序排序,每一列都按照从上到下递增的顺序排序.请完成一个函数,输入这样的一个二维数组和一个整数,判断数组中是否含有该整数. 示例: 现 ...

  7. HDU - 3068 最长回文(manacher算法)

    题意:给出一个只由小写英文字符a,b,c...y,z组成的字符串S,求S中最长回文串的长度. 分析: manacher算法: 1.将字符串中每个字符的两边都插入一个特殊字符.(此操作的目的是,将字符串 ...

  8. realme发布会将于本周三在北京·幻艺术举行

    5月13日消息,realme宣布将于5月15日(本周三)在北京·幻艺术中心发布realme X. 根据官方公布的海报,realme X采用了升降全面屏方案,无挖孔.无刘海.无水滴,支持屏幕指纹识别,正 ...

  9. POJ 2226 缩点建图+二分图最大匹配

    这个最小覆盖但不同于 POJ 3041,只有横或者竖方向连通的点能用一块板子覆盖,非连续的,就要用多块 所以用类似并查集方法,分别横向与竖向缩点,有交集的地方就连通,再走一遍最大匹配即可 一开始还有点 ...

  10. centos7 lvm合并分区脚本初探-linux性能测试 -centos7修改网卡名字-jdk环境安装脚本-关键字查询文件-批量添加用户

    1.#!/bin/bash lvmdiskscan | grep centos > /root/a.txt a=`sed -n '1p' /root/a.txt` b=`sed -n '2p' ...