1.Thread类

普通调用

t = Thread(target=test, args=(i,))	# test为目标函数名, 若函数需要参数将其以元组形									                # 式赋给args, 若无参数可不写
t.start() # 用start()函数开启线程

例子

import time
from threading import Thread # 目标函数
def test(i):
print("hello ", i)
time.sleep(1) def main():
# 循环5次,开起五个线程
for i in range(5):
t = Thread(target=test, args=(i,))
t.start() if __name__ == '__main__':
main()

继承Thread类

定义一个自己的类继承自Thread,重写run()方法,即 将原本执行任务的函数内容移植到run()方法中.可通过类的属性传参.

例子

from threading import Thread
import time class MyThread(Thread):
def __init__(self, i):
Thread.__init__(self) # 初始化父类"构造函数"
self.i = i # 初始化,目的将run函数参数作为类的属性 def run(self):
time.sleep(1)
msg = "I'm " + self.name + " @ " + str(self.i)
print(msg) def main():
for i in range(3): # 开启三个线程
t = MyThread(i) # 实例化自己的类
t.start() if __name__ == '__main__':
main()

线程的执行顺序

上面的例子中线程的执行顺序是随机的

2.线程间共享全局变量

下面例子中test1()和test2()共享g_num全局变量.希望test1()执行的结果是1000000,test2()执行的结果是2000000.但是time.sleep()函数会影响结果.

from threading import Thread
import time g_num = 0 def test1():
global g_num
for i in range(1000000):
g_num += 1 print('---test1 g_num is %d---' % g_num) def test2():
global g_num
for i in range(1000000):
g_num += 1 print('---test2 g_num is %d---' % g_num) t1 = Thread(target=test1)
t1.start() # time.sleep(3) # 运行这句话与不运行这句话结果不一样 t2 = Thread(target=test2)
t2.start() print('-----g_num: %d-----' % g_num)

不执行sleep函数的结果(可能出现多种不同的运行结果)

---test1 g_num is 1312283---
-----g_num: 1312283-----
---test2 g_num is 1341534---

执行sleep()函数的结果

---test1 g_num is 1000000---
-----g_num: 1079982-----
---test2 g_num is 2000000---

其实这也不难理解,sleep之后test1中的任务肯定先完成,而不执行sleep两个函数同能对g_num同时操作

通过轮询的方式解决线程间共享全局变量的问题

from threading import Thread

g_num = 0
g_flag = 1 # 增加一个标识全局变量 def test1():
global g_num
global g_flag
if g_flag == 1:
for i in range(1000000):
g_num += 1
g_flag = 0
print('---test1 g_num is %d---' % g_num) def test2():
global g_num # 轮询
while True:
if g_flag != 1: # 一旦test1()执行完,即g_flag = 0时,test2()开始执行累加g_num操作
for i in range(1000000):
g_num += 1
break print('---test2 g_num is %d---' % g_num) t1 = Thread(target=test1)
t1.start() t2 = Thread(target=test2)
t2.start() print('-----g_num: %d-----' % g_num)

运行结果

-----g_num: 303721-----
---test1 g_num is 1000000---
---test2 g_num is 2000000---

第二个线程一开始并没有执行累加g_num的操作,而是先进行一个死循环,在这个循环中不断的"询问"g_flag的值是否不等于1.一但g_flag不等于1,即test1()结束后便开始干"正事".

通过互斥锁解决线程间共享全局变量的问题

from threading import Thread, Lock  # 导入互斥锁

g_num = 0

def test1():
global g_num for i in range(1000000):
mutex.acquire() # 上锁,此时其他的锁会等待 上锁应该遵循最小原则
g_num += 1
mutex.release() # 开锁,此时其他的锁会抢着开锁 print('---test1 g_num is %d---' % g_num) def test2():
global g_num for i in range(1000000):
mutex.acquire()
g_num += 1
mutex.release() print('---test2 g_num is %d---' % g_num) # 创建一把互斥锁,默认不上锁
mutex = Lock() t1 = Thread(target=test1)
t1.start() t2 = Thread(target=test2)
t2.start() print('-----g_num: %d-----' % g_num)

运行结果

-----g_num: 45012-----
---test1 g_num is 1979942---
---test2 g_num is 2000000---

从结果可以看出test2()的结果是正确的,而test1()的结果很接近test2.这也不难理解.互斥锁会把夹在中间的部分锁定,也就是说,在极短时间内只能有一个线程在执行该代码.一旦开锁了(release),所有线程开始抢这把锁,某个线程抢到之后会把自己的操作锁住,其他线程只能等待,一直反复直至全部任务完成.

只有对上述代码稍微修改便可以实现我们想要的结果

修改后的代码

from threading import Thread, Lock  # 导入互斥锁

g_num = 0

def test1():
global g_num mutex.acquire() # 上锁,此时其他的锁会等待 上锁应该遵循最小原则
for i in range(1000000):
g_num += 1
mutex.release() # 开锁,此时其他的锁会抢着开锁 print('---test1 g_num is %d---' % g_num) def test2():
global g_num mutex.acquire()
for i in range(1000000):
g_num += 1
mutex.release() print('---test2 g_num is %d---' % g_num) # 创建一把互斥锁,默认不上锁
mutex = Lock() t1 = Thread(target=test1)
t1.start() t2 = Thread(target=test2)
t2.start() print('-----g_num: %d-----' % g_num)

结果

-----g_num: 220254-----
---test1 g_num is 1000000---
---test2 g_num is 2000000---

值得注意的是,互斥锁上的范围太大就失去了线程的意义,别的线程都把时间浪费在了等待上.轮询同理.

3.线程间使用非全局变量

from threading import Thread
import threading
import time def test1():
name = threading.current_thread().name # 获取当前线程名字
print('----thread name is %s----' % name)
g_num = 100
if name == 'Thread-1':
g_num += 1
else:
time.sleep(2)
print('---thread is %s | g_num is %d---' % (name, g_num)) t1 = Thread(target=test1)
t1.start() t2 = Thread(target=test1)
t2.start()

运行结果

----thread name is Thread-1----
---thread is Thread-1 | g_num is 101---
----thread name is Thread-2----
---thread is Thread-2 | g_num is 100---

非全局对于同一个函数来说.可以通过线程的名字来区分.

4.线程死锁

import threading
import time class MyThread1(threading.Thread):
def run(self):
if mutexA.acquire():
print(self.name + '---do1---up---')
time.sleep(1) if mutexB.acquire():
print(self.name + '---do1---down---')
mutexB.release()
mutexA.release() class MyThread2(threading.Thread):
def run(self):
if mutexB.acquire():
print(self.name + '---do2---up---')
time.sleep(1) if mutexA.acquire():
print(self.name + '---do2---down---')
mutexA.release()
mutexB.release() if __name__ == '__main__':
mutexA = threading.Lock()
mutexB = threading.Lock()
t1 = MyThread1()
t2 = MyThread2()
t1.start()
t2.start()

运行结果(卡在了这两句,未结束)

Thread-1---do1---up---
Thread-2---do2---up---

分析代码,t1的代码在等待mutexB解锁的时候t2在等待mutexA解锁.而t1必须先执行完mutexB锁中的代码执行完才能释放mutexA,t2必须先执行完mutexA锁中的代码执行完才能释放mutexB,这就导致两个线程一直等待下去形成死锁,会浪费CPU资源.

解决死锁的办法

设置超时时间 mutexA.acquire(2)
当然也可以从算法上避免死锁

5.使用ThreadLocal

import threading

# 创建全局ThreadLocal对象
local_school = threading.local() def process_student():
# 获取当前线程相关联的student
std = local_school.student
print('Hello, %s in %s' % (std, threading.current_thread().name)) def process_thread(name):
# 绑定ThreadLocal的student
local_school.student = name
process_student() t1 = threading.Thread(target=process_thread, args=('kain',), name='Thread-A')
t2 = threading.Thread(target=process_thread, args=('huck',), name='Thread-B') t1.start()
t2.start()
t1.join()
t2.join()

运行结果

Hello, kain in Thread-A
Hello, huck in Thread-B

6.生产者与消费者问题

import threading
import time # Python2
# from Queue import Queue # Python3
from queue import Queue class Producer(threading.Thread):
def run(self):
global queue
count = 0
while True:
if queue.qsize() < 1000:
for i in range(100):
count += 1
msg = '生成产品' + str(count)
queue.put(msg)
print(msg)
time.sleep(0.5) class Consumer(threading.Thread):
def run(self):
global queue
while True:
if queue.qsize() > 100:
for i in range(3):
msg = self.name + '消费了' + queue.get()
print(msg)
time.sleep(1) if __name__ == '__main__':
queue = Queue() for i in range(500):
queue.put('初始产品'+str(i)) # 向队列中塞内容 for i in range(2):
p = Producer()
p.start() for i in range(5):
c = Consumer()
c.start()

运行结果过长不予展示

Python多线程,线程死锁及解决,生产者与消费者问题的更多相关文章

  1. 多线程-线程间通信-多生产者多消费者问题(JDK1.5后Lock,Condition解决办法及开发中代码范例)

    1 package multithread4; 2 3 import java.util.concurrent.locks.Condition; 4 import java.util.concurre ...

  2. 多线程-线程间通信-多生产者多消费者问题解决(notifyAll)

    1 package multithread4; 2 3 /* 4 * 生产者,消费者. 5 * 6 * 多生产者,多消费者的问题. 7 * 8 * if判断标记,只有一次,会导致不该运行的线程运行了. ...

  3. python 多线程笔记(5)-- 生产者/消费者模式

    我们已经知道,对公共资源进行互斥访问,可以使用Lock上锁,或者使用RLock去重入锁. 但是这些都只是方便于处理简单的同步现象,我们甚至还不能很合理的去解决使用Lock锁带来的死锁问题. 要解决更复 ...

  4. python开发线程:死锁和递归锁&信号量&定时器&线程queue&事件evevt

    一 死锁现象与递归锁 进程也有死锁与递归锁,在进程那里忘记说了,放到这里一切说了额 所谓死锁: 是指两个或两个以上的进程或线程在执行过程中,因争夺资源而造成的一种互相等待的现象,若无外力作用,它们都将 ...

  5. java多线程(同步与死锁问题,生产者与消费者问题)

    首先我们来看同步与死锁问题: 所谓死锁,就是A拥有banana.B拥有apple. A对B说:你把apple给我.我就把banana给你. B对A说:你把banana给我,我就把apple给你. 可是 ...

  6. Python 多线程 线程安全、daemon简介 (四)

    线程安全 只能在Winodws下的ipython中演示,Python命令行.Pycharm.Mac下的ipython都演示不出效果 import threading def worker(): for ...

  7. python多线程编程-queue模块和生产者-消费者问题

    摘录python核心编程 本例中演示生产者-消费者模型:商品或服务的生产者生产商品,然后将其放到类似队列的数据结构中.生产商品中的时间是不确定的,同样消费者消费商品的时间也是不确定的. 使用queue ...

  8. Java多线程-----线程安全及解决机制

    1.什么是线程安全问题? 从某个线程开始访问到访问结束的整个过程,如果有一个访问对象被其他线程修改,那么对于当前线程而言就发生了线程安全问题: 如果在整个访问过程中,无一对象被其他线程修改,就是线程安 ...

  9. java 中多线程之间的通讯之生产者和消费者 (多个线程之间的通讯)

    在真实开发 中关于多线程的通讯的问题用到下边的例子是比较多的 不同的地方时if 和while 的区别 如果只是两个线程之间的通讯,使用if是没有问题的. 但是在多个线程之间就会有问题 /* * 这个例 ...

随机推荐

  1. git使用问题二删除远程仓库文件,本地保留不动

    git rm --cached filename/-r directory git commit "xxxx" git push

  2. pytorch max和clamp

    torch.max() torch.max(a):数组a的最大值 torch.max(a, dim=1):多维数组沿维度1方向上的最大值,若a为二维数组,则为每行的最大值(此时是对每行的每列值比较取最 ...

  3. 该虚拟机似乎正在使用中 如果该虚拟机未在使用请按获取所权T按钮获取他的所有权,否则,请按取消按钮以防损坏

    虚拟机出现如下情况 不能够正常过使用,解决办法如下:直接进入上图中配置文件的目录下,然后删除所有的lck结尾的文件夹,然后重新启动, 然后file打开这个文件即可.重新进入虚拟机开机.

  4. 用BusyBox制作Linux最小系统

    1.下载busybox-1.30.1 地址:https://busybox.net/downloads/busybox-1.30.1.tar.bz2 2.解压:tar xvf busybox-1.30 ...

  5. Java连载79-Calendar解析

    一. Calendar解析 package com.bjpowernode.java_learning; import java.util.Date; import java.text.ParseEx ...

  6. 自己写个tween

    public Vector3 begin,end;//起始终止坐标 public float BtoE_time;//用时 float timer,lerp;//计时器和进度值 void Update ...

  7. Django(四) 后台管理:创建管理员、注册模型类、自定义管理页面显示内容

    后台管理 第1步.本地化:设置语言.时区 修改project1/settings.py #LANGUAGE_CODE = 'en-us' LANGUAGE_CODE = 'zh-hans' #设置语言 ...

  8. 01 DDL(DataDefinitionLanguage)

    注: 语句用 ; 或 \g \G 表示结束 .       建库语句 :         CREATE DATABASE db_name ;          查询有哪些库 :         SHO ...

  9. 官网英文版学习——RabbitMQ学习笔记(十)RabbitMQ集群

    在第二节我们进行了RabbitMQ的安装,现在我们就RabbitMQ进行集群的搭建进行学习,参考官网地址是:http://www.rabbitmq.com/clustering.html 首先我们来看 ...

  10. 官网英文版学习——RabbitMQ学习笔记(六)Routing

    有选择的接收消息. 上一节我们使用的是fanout exchange来实现消息的发布/订阅模式,这并没有给我们带来多大的灵活性——它只能够让人盲目地进行广播.而本节我们采用direct类型的交换器来实 ...