多线程能干什么:

生产者消费者问题:
一直生产,一直消费,中间有阀值,避免供求关系不平衡,导致出现问题。
#线程安全问题,要是线程同时来,听谁的
#锁:一种数据结构 队列:先进线出 栈:先进后出

1.解耦(让程序各模块之间的关联性降到最低)
2.支持并发:
生产者消费者是两个独立的并发体,他们之间是用缓冲区作为桥梁连接,只需要从缓冲区里拿数据即可,这样就不会因为
彼此速度而发生阻塞。
3.支持忙闲不均
如果制造数据的速度时快时慢,缓冲区的好处就体现出来了,当数据制造快的时候,消费者来不及处理,未处理的数据可
以暂时存在缓冲区中,等生产者的速度慢下来,消费者再慢慢处理.

一个简单的多任务问题,单线程导致效率低下:

from time import ctime,sleep


def music(a):
for i in range(2):
print("I was listening to %s.%s" %(a,ctime()))
sleep(2)

def movie(b):
for i in range(2):
print("I was watching to %s.%s" %(b,ctime()))
sleep(2)

music("告白气球")
movie("泰坦尼克号")
print("all over %s" %ctime())

python多线程问题及生产者消费者示例_多线程

将多任务问题用多线程解决,提高效率,解决了多任务等待的问题:

from threading import Thread
from time import ctime,sleep


def music(a):
for i in range(2):
print("I was listening to %s.%s" %(a,ctime()))
sleep(2)


def movie(b):
for i in range(2):
print("I was watching to %s.%s" %(b,ctime()))
sleep(5)

# 调用多线程解决多任务问题
t1 = Thread(target=music,args=("a"))
t1.start()
t2 = Thread(target=movie,args=("b"))
t2.start()

python多线程问题及生产者消费者示例_子线程_02

线程的定义:

线程是操作系统能够进行运算调度的最小单位。它被包括在进程之中,是进程的实际运作单位。一个进程可以并发多个线程,
每条线程并行执行不同的任务(线程是进程的一个实体,是被系统独立调度和分派的基本单元)
每一个进程启动时都会最先产生一个线程,即主线程,然后主线程会再创建其他的子线程。

通常,我们写服务器处理模型的程序时,有以下几种模型:

(1)每收到一个请求,创建一个新的进程,来处理该请求;
(2)每收到一个请求,创建一个新的线程,来处理该请求;
(3)每收到一个请求,放入一个事件列表,让主进程通过非阻塞I/O方式来处理请求;
上面的几种方式,各有千秋,
第(1)中方法,由于创建新的进程的开销比较大,所以,会导致服务器性能比较差,但实现比较简单。
第(2)种方式,由于要涉及到线程的同步,有可能会面临死锁等问题。
第(3)种方式,在写应用程序代码时,逻辑比前面两种都复杂。
综合考虑各方面因素,一般普遍认为第(3)种方式是大多数网络服务器采用的方式

线程的缺点:

在UI编程中,常常要对鼠标点击进行相应,首先如何获得鼠标点击呢?
方式一:创建一个线程,该线程一直循环检测是否有鼠标点击,
那么这个方式有以下几个缺点:
CPU资源浪费,可能鼠标点击的频率非常小,但是扫描线程还是会一直循环检测,这会造成很多的CPU资源浪费;
如果扫描鼠标点击的接口是阻塞的呢?
如果是堵塞的,又会出现下面这样的问题,如果我们不但要扫描鼠标点击,还要扫描键盘是否按下,由于扫描鼠标时被
堵塞了,那么可能永远不会去扫描键盘;如果一个循环需要扫描的设备非常多,这又会引来响应时间的问题;
所以,该方式是非常不好的。

多线程示例01:

from threading import Thread


def Foo(arg):
print arg

print 'before'
# 线程和函数建立关系
t1 = Thread(target=Foo, args=(1,))
t1.start()
# getName()是获得调用这个方法的线程的名字
print t1.getName()
print 'after'

python多线程问题及生产者消费者示例_子线程_03

多线程示例02:

from threading import Thread
import time


def Foo():
for item in range(4):
print(item)
time.sleep(2)
print('before')
t1=Thread(target=Foo)
t1.start()
print('after')

python多线程问题及生产者消费者示例_多线程_04

多线程02升级版(加入了t1.setDaemon(True)):

意味着主线程不需要等待子线程啥时候结束就可以结束 但是会等到子线程结束之后一起销毁

from threading import Thread
import time


def Foo():
for item in range(4):
print item
time.sleep(2)
print 'before'
t1 = Thread(target=Foo)
t1.setDaemon(True)
t1.start()
print 'after'
# 这里主线程等待时间是5秒,所以子线程只能够输出0,1,2,但是当主线程时间调整为10秒时,子线程会全部输出0,1,2,3
time.sleep(5)

python多线程问题及生产者消费者示例_子线程_05

多线程03:加入了t1.join(5),表示主线程到join()就不往下走了,直到子线程进行完成

from threading import Thread
import time


def Foo():
for item in range(4):
print(item)
time.sleep(2)
print('before')
t1 = Thread(target=Foo)
t1.start()
# 主线程到join()就不往下走了,直到子线程进行完成
t1.join(5)
# 这句代码意味着主线程主会等待子线程5秒之后就结束了,不管子线程是否完成
# 所以输出子线程中的输出3在主线程输出的after后面
print('after')

python多线程问题及生产者消费者示例_多线程_06

操作系统(生产者消费者问题)

import threading
import queue

# 定义生产者类
def Producer(name,que):
que.put('baozi')
print('%s:Made a baozi...' % name)

# 定义消费者类
def Consumer(name,que):
que.get()
print('%s:Got a baozi...' % name)


# 创建队列
q = queue.Queue()
# 定义两个生产者
p1 = threading.Thread(target=Producer,args=['chef1',q])
p2 = threading.Thread(target=Producer,args=['chef2',q])
p1.start()
p2.start()
# 定义两个消费者
c1 = threading.Thread(target=Consumer,args=['tom',q])
c2 = threading.Thread(target=Consumer,args=['harry',q])
c1.start()
c2.start()

python多线程问题及生产者消费者示例_主线程_07

操作系统升级版01:实现了动态化生产和消费

import threading
import queue
import time
import random

# 定义生产者类
def Producer(name,que):
# 加入循环使之一直处于生产当中
while True:
que.put('baozi')
print('%s:Made a baozi...' % name)
# 调用函数使之随机睡眠实现动态化
time.sleep(random.randrange(3))


# 定义消费者类
def Consumer(name,que):
# 加入循环使之一直处于消费当中
while True:
que.get()
print('%s:Got a baozi...' % name)
# 调用函数使之随机睡眠实现动态化
time.sleep(random.randrange(3))

# 创建队列
q = queue.Queue()
# 定义两个生产者
p1 = threading.Thread(target=Producer,args=['chef1',q])
p2 = threading.Thread(target=Producer,args=['chef2',q])
p1.start()
p2.start()
# 定义两个消费者
c1 = threading.Thread(target=Consumer,args=['tom',q])
c2 = threading.Thread(target=Consumer,args=['harry',q])
c1.start()
c2.start()

python多线程问题及生产者消费者示例_子线程_08

操作系统升级版02版加入了que.get_nowait()加入消费者不等待会报错

仅仅加了que.get_nowait()方法,其他代码未作更改
# 定义消费者类
def Consumer(name,que):
# 加入循环使之一直处于消费当中
while True:
# 当队列为空时,再调用get函数,程序会阻塞,导致无法正常执行后面的代码,程序也不会退出,可以用get_nowait函数,
# 当队列为空,不会等待,直接抛出异常,
que.get_nowait()
print('%s:Got a baozi...' % name)
# 调用函数使之随机睡眠实现动态化
time.sleep(random.randrange(3))

python多线程问题及生产者消费者示例_主线程_09

操作系统升级版03版,发生供不应求时抛出异常的话加入捕获异常

仅仅加了捕获异常代码,其他代码没做更改:
# 定义消费者类
def Consumer(name,que):
# 加入循环使之一直处于消费当中
while True:
try:
# 当队列为空时,再调用get函数,程序会阻塞,导致无法正常执行后面的代码,程序也不会退出,可以用get_nowait函数,
# 当队列为空,不会等待,直接抛出异常,
que.get_nowait()
print('%s:Got a baozi...' % name)
except Exception:
print("没有包子了")
# 调用函数使之随机睡眠实现动态化
time.sleep(random.randrange(3))

python多线程问题及生产者消费者示例_多线程_10

操作系统升级版04:实现智能化,生产者会合理的生产并对库存进行提醒,消费者会等待,实现合理化。

修改生产者类使之有对库存的智能提醒
# 定义生产者类
def Producer(name,que):
# 加入循环使之一直处于生产当中
while True:
if que.qsize() < 3:
que.put('baozi')
print('%s:Made a baozi...' % name)
else:
print("还有三个包子呢")
# 调用函数使之随机睡眠实现动态化
time.sleep(random.randrange(3))

python多线程问题及生产者消费者示例_子线程_11

异步的方式:模拟生活,使得生产者和消费者有更加智能的交互

import threading
import time

def Producer():
print('chef:等人来买包子')
# 收到了消费者的event.set 也就是把这个flag改为了true,但是我们的包子并没有做好
event.wait()
# 此时应该将flag的值改回去
event.clear()
print('chef:someone is coming for 包子')
print('chef:making a 包子 for someone')
time.sleep(5)
# 告诉人家包子做好了
print('你的包子好了~')
event.set()

def Consumer():
print('tom:去买包子')
# 告诉人家我来了
event.set()
time.sleep(2)
print('tom:waiting for 包子 to be ready')
# 我在不断检测,但我已经不阻塞了
while True:
if event.is_set():
print('Thanks~')
break
else:
print('怎么还没好呀~')
# 模拟正在做自己的事情
time.sleep(1)
event = threading.Event()

p1 = threading.Thread(target=Producer)
c1 = threading.Thread(target=Consumer)
p1.start()
c1.start()

python多线程问题及生产者消费者示例_主线程_12

事件驱动模型:

事件驱动编程是一种编程范式,这里程序的执行流由外部事件来决定。
它的特点是包含一个事件循环,当外部事件发生时使用回调机制来触发相应的处理。
另外两种常见的编程范式是(单线程)同步以及多线程编程。
目前大部分的UI编程都是事件驱动模型,如很多UI平台都会提供onClick()事件,这个事件就代表鼠标按下事件。
事件驱动模型大体思路如下:
1. 有一个事件(消息)队列;
2. 鼠标按下时,往这个队列中增加一个点击事件(消息);
3. 有个循环,不断从队列取出事件,根据不同的事件,调用不同的函数,如onClick()、onKeyDown()等;
4. 事件(消息)一般都各自保存各自的处理函数指针,这样,每个消息都有独立的处理函数;

事件驱动方式更智能化的让生产者和消费者交互:

改变消费者类
def Consumer():
print('tom:去买包子')
# 告诉人家我来了
event.set()
time.sleep(2)
print('tom:waiting for 包子 to be ready')
# 我在不断检测,但我已经不阻塞了
event.wait()
print('哎呀~真好吃')

python多线程问题及生产者消费者示例_子线程_13

给线程加锁可以防止输出异常

lock在不同线程使用同一共享内存时,能够确保线程之间互不影响,使用lock的方法是, 在每个线程执行运算修改共享内存之前,执行lock.acquire()将共享内存上锁, 确保当前线程执行时,内存不会被其他线程访问,执行运算完毕后,使用lock.release()将锁打开, 保证其他的线程可以使用该共享内存。
import threading


def job1():
global A, lock
lock.acquire()
for i in range(10):
A += 1
print('job1', A)
lock.release()


def job2():
global A, lock
lock.acquire()
for i in range(10):
A += 10
print('job2', A)
lock.release()


if __name__ == '__main__':
lock = threading.Lock()
A = 0
t1 = threading.Thread(target=job1)
t2 = threading.Thread(target=job2)
t1.start()
t2.start()
t1.join()
t2.join()

python多线程问题及生产者消费者示例_多线程_14