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. C++路径的整理

    写C++,路径的问题一直都让人很头疼,抽空整理一些方法:也许以后会用到: 1."./" 加不加都一样,就是指当前目录 2."../" 表示当前目录的上级目录,即 ...

  2. 文本编辑器vim/vi——末行模式

    指令格式: #vim 文件路径作用:打开指定的文件. 进入方式:由命令模式进入,按下“:”或者“/(表示查找)”即可进入 退出方式: a. 按下esc b. 连按2次esc键 c. 删除末行全部输入字 ...

  3. (转)深入理解JVM—JVM内存模型

    原文地址:http://www.cnblogs.com/dingyingsi/p/3760447.html 我们知道,计算机CPU和内存的交互是最频繁的,内存是我们的高速缓存区,用户磁盘和CPU的交互 ...

  4. 记录一次ajax使用

    function getSSOUrl(urlvalue) { var urls; $.ajax({ type: "GET", url: "../../GeoManage/ ...

  5. 吴裕雄--天生自然C++语言学习笔记:C++ 继承

    当创建一个类时,您不需要重新编写新的数据成员和成员函数,只需指定新建的类继承了一个已有的类的成员即可.这个已有的类称为基类,新建的类称为派生类. 继承代表了 is a 关系.例如,哺乳动物是动物,狗是 ...

  6. mysql第五篇 : MySQL 之 视图、触发器、存储过程、函数、事物与数据库锁

    第五篇 : MySQL 之 视图.触发器.存储过程.函数.事物与数据库锁 一.视图 视图是一个虚拟表(非真实存在的),其本质是‘根据SQL语句获取动态的数据集,并为其命名‘ ,用户使用时只需使用“名称 ...

  7. P 1020 月饼

    转跳点:

  8. taro编译的时候报 exports.pRimraf = util_1.promisify(rimraf); 错误

    C:\Users\1\AppData\Roaming\npm\node_modules\@tarojs\cli\dist\h5\helper.js:8 exports.pRimraf = util_1 ...

  9. POJ 3253:Fence Repair

    Fence Repair Time Limit: 2000MS   Memory Limit: 65536K Total Submissions: 33114   Accepted: 10693 De ...

  10. CGridCtrl显示图片