现代操作系统比如Mac OS X,UNIX,Linux,Windows等,都是支持“多任务”的操作系统。
什么叫“多任务”呢?简单地说,就是操作系统可以同时运行多个任务。打个比方,你一边在用浏览器上网,一边在听MP3,一边在用Word赶作业,这就是多任务,至少同时有3个任务正在运行。还有很多任务悄悄地在后台同时运行着,只是桌面上没有显示而已。
现在,多核CPU已经非常普及了,但是,即使过去的单核CPU,也可以执行多任务。由于CPU执行代码都是顺序执行的,那么,单核CPU是怎么执行多任务的呢?
答案就是操作系统轮流让各个任务交替执行,任务1执行0.01秒,切换到任务2,任务2执行0.01秒,再切换到任务3,执行0.01秒……这样反复执行下去。表面上看,每个任务都是交替执行的,但是,由于CPU的执行速度实在是太快了,我们感觉就像所有任务都在同时执行一样。
真正的并行执行多任务只能在多核CPU上实现,但是,由于任务数量远远多于CPU的核心数量,所以,操作系统也会自动把很多任务轮流调度到每个核心上执行。
对于操作系统来说,一个任务就是一个进程(Process),比如打开一个浏览器就是启动一个浏览器进程,打开一个记事本就启动了一个记事本进程,打开两个记事本就启动了两个记事本进程,打开一个Word就启动了一个Word进程。
有些进程还不止同时干一件事,比如Word,它可以同时进行打字、拼写检查、打印等事情。在一个进程内部,要同时干多件事,就需要同时运行多个“子任务”,我们把进程内的这些“子任务”称为线程(Thread)。
由于每个进程至少要干一件事,所以,一个进程至少有一个线程。当然,像Word这种复杂的进程可以有多个线程,多个线程可以同时执行,多线程的执行方式和多进程是一样的,也是由操作系统在多个线程之间快速切换,让每个线程都短暂地交替运行,看起来就像同时执行一样。当然,真正地同时执行多线程需要多核CPU才可能实现。
我们前面编写的所有的Python程序,都是执行单任务的进程,也就是只有一个线程。如果我们要同时执行多个任务怎么办?
有三种解决方案:
一种是启动多个进程,每个进程虽然只有一个线程,但多个进程可以一块执行多个任务。
还有一种方法是启动一个进程,在一个进程内启动多个线程,这样,多个线程也可以一块执行多个任务。
当然还有第三种方法,就是启动多个进程,每个进程再启动多个线程,这样同时执行的任务就更多了
总结一下就是,多任务的实现有3种方式:
- 多进程模式;
- 多线程模式;
- 多进程+多线程模式。
线程是最小的执行单元,而进程由至少一个线程组成。如何调度进程和线程,完全由操作系统决定,程序自己不能决定什么时候执行,执行多长时间。
多进程和多线程的程序涉及到同步、数据共享的问题,编写起来更复杂。
关于进程和线程,大家总结一句话是“进程是操作系统分配资源的最小单元,线程是操作系统调度的最小单元”。
进程是具有一定独立功能的程序关于某个数据集合上的一次运行活动,进程是系统进行资源分配和调度的一个独立单位.
线程是进程的一个实体,是CPU调度和分派的基本单位,它是比进程更小的能独立运行的基本单位.线程自己基本上不拥有系统资源,只拥有一点在运行中必不可少的资源(如程序计数器,一组寄存器和栈),但是它可与同属一个进程的其他的线程共享进程所拥有的全部资源.
一个线程可以创建和撤销另一个线程;同一个进程中的多个线程之间可以并发执行.
进程和线程的主要差别在于它们是不同的操作系统资源管理方式。进程有独立的地址空间,一个进程崩溃后,在保护模式下不会对其它进程产生影响,而线程只是一个进程中的不同执行路径。线程有自己的堆栈和局部变量,但线程之间没有单独的地址空间,一个线程死掉就等于整个进程死掉,所以多进程的程序要比多线程的程序健壮,但在进程切换时,耗费资源较大,效率要差一些。但对于一些要求同时进行并且又要共享某些变量的并发操作,只能用线程,不能用进程。
#coding=gbk
#多线程编程
#使用_thread 模块 的 start_new_thread 函数
# import _thread,random,time
# def timer(iternal):
# for i in range(5):
# time.sleep(random.choice(range(iternal)))
# thread_id=_thread.get_ident()
# print('Thread: {0} , Time: {1}'.format(thread_id, time.ctime()))
# def test():
# _thread.start_new_thread(timer(3), (5,))
# _thread.start_new_thread(timer(3),(5,))
# if __name__ == '__main__':
# test()
#注意:要避免使用_thread模块
print()
threading模块:

#自定义派生于Thread的对象
import threading,random,time
class MyThread(threading.Thread):
def __init__(self,interval):
threading.Thread.__init__(self)
self.interval=interval
def run(self):
for i in range(5):
time.sleep(random.choice(range(self.interval)))#随机休眠了0-interval秒
thread_id=threading.get_ident()
print('Thread: {0} , Time: {1}'.format(thread_id, time.ctime()))
if __name__ == '__main__':
t1=MyThread(5)
t2=MyThread(5)#创建了2个线程
t1.start()
t2.start()
print(threading.active_count())
#输出:
# Thread: 3148 , Time: Tue Jun 5 15:56:11 2018
# Thread: 8916 , Time: Tue Jun 5 15:56:12 2018
# Thread: 3148 , Time: Tue Jun 5 15:56:13 2018
# Thread: 8916 , Time: Tue Jun 5 15:56:14 2018
# Thread: 8916 , Time: Tue Jun 5 15:56:14 2018
# Thread: 8916 , Time: Tue Jun 5 15:56:14 2018
# Thread: 3148 , Time: Tue Jun 5 15:56:14 2018
# Thread: 8916 , Time: Tue Jun 5 15:56:15 2018
# Thread: 3148 , Time: Tue Jun 5 15:56:18 2018
# Thread: 3148 , Time: Tue Jun 5 15:56:19 2018
#线程join的使用方法
import threading,random,time
class MyThread1(threading.Thread):
def __init__(self):
threading.Thread.__init__(self)
def run(self):
for i in range(5): #设置运行5秒
time.sleep(1)
t=threading.current_thread()
print(' Thread {0} ,at Time: {1}'.format(t.name,time.ctime()))
print("t1线程结束")
def test1():
t1=MyThread1()
t1.name='t1' #设置线程名称
t1.start()
print('当前线程 t1运行 2 秒')
t1.join(2)
print('当前线程t1运行2 秒 完成')
t1.join()
print("主线程结束")
test1()
#运行结果;
# 当前线程 t1运行 2 秒
# Thread t1 ,at Time: Tue Jun 5 16:17:12 2018
# Thread t1 ,at Time: Tue Jun 5 16:17:13 2018
# 当前线程t1运行2 秒 完成
# Thread t1 ,at Time: Tue Jun 5 16:17:14 2018
# Thread t1 ,at Time: Tue Jun 5 16:17:15 2018
# Thread t1 ,at Time: Tue Jun 5 16:17:16 2018
# t1线程结束
# 主线程结束
#线程同步,模拟银行取款,如果多个线程同时取款,则可能造成取款混乱,这就需要线程同步
#模拟一个人取款
import threading,random,time
class Account(threading.Thread):
lock=threading.Lock()
def __init__(self,account):
threading.Thread.__init__(self)
Account.account=account
def run(self):
self.withdraw()
def withdraw(self):
Account.lock.acquire() #获得锁
t=threading.current_thread()
a=random.choice(range(50,101)) #取款
if Account.account < a :
print('{0}交易失败,账户余额:{1}, 取款为: {2}'.format(t.name,Account.account,a))
Account.lock.release() #释放锁
return 0
time.sleep(random.choice(range(3))) #此句在写程序时错误,写成random.choice(random(5))
prev=Account.account
Account.account-=a
print('{0}的账户余额:{1}, 取款为: {2},剩余账户余额: {3}'.format(t.name,prev,a,Account.account))
Account.lock.release()
def test():
for i in range(5):
Account(75).start() #开始时,账户只有75元,取5次
test()
#输出为:
# Thread-1交易失败,账户余额:75, 取款为: 84
# Thread-2的账户余额:75, 取款为: 73,剩余账户余额: 2
# Thread-3交易失败,账户余额:2, 取款为: 79
# Thread-4交易失败,账户余额:2, 取款为: 97
# Thread-5交易失败,账户余额:2, 取款为: 78
避免死锁:

#线程间的通信。threading.Condition()
#消费者-生产者模式,生产者生产一件,消费者再消费一件
import threading,time,random
class Container():
def __init__(self):
self.contents=0
self.available=False
self.cv=threading.Condition() #条件变量
def put(self,value):
with self.cv: #使用条件变量
if self.available :
self.cv.wait()
self.contents=value
t=threading.current_thread()
print('{0}生产了: {1}'.format(t.name,self.contents))
self.available=True
self.cv.notify() #通知等待的消费者
def get(self):
with self.cv:
if not self.available:
self.cv.wait()
t=threading.current_thread()
print('{0}消费了: {1}'.format(t.name,self.contents))
self.available=False
self.cv.notify() #通知
class Producer(threading.Thread):
def __init__(self,container):
threading.Thread.__init__(self)
self.container=container
def run(self):
for i in range(5):
time.sleep(random.choice(range(5)))
self.container.put(i)
class Consumer(threading.Thread):
def __init__(self,container):
threading.Thread.__init__(self)
self.container=container
def run(self):
for i in range(5):
time.sleep(random.choice(range(5)))
self.container.get()
def test():
container=Container()
Producer(container).start()
Consumer(container).start()
test()
#输出为:
# Thread-1生产了: 0
# Thread-2消费了: 0
# Thread-1生产了: 1
# Thread-2消费了: 1
# Thread-1生产了: 2
# Thread-2消费了: 2
# Thread-1生产了: 3
# Thread-2消费了: 3
# Thread-1生产了: 4
# Thread-2消费了: 4