进程与线程

Posted samyoung

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了进程与线程相关的知识,希望对你有一定的参考价值。

操作系统

1.为什么要有操作系统
操作系统,位于底层硬件与应用软件之间的一层
工作方式:向下管理硬件,向上提供接口

操作系统进程切换:
1.出现IO操作
2.固定时间

进程

1.定义

进程就是一个程序在一个数据集上的一次动态执行过程。进程一般由程序、数据集、进程控制块三部分组成。我们编写的程序用来描述进程要完成哪些功能以及如何完成;数据集则是程序在执行过程中所需要使用的资源;进程控制块用来记录进程的外部特征,描述进程的执行变化过程,系统可以利用它来控制和管理进程,它是系统感知进程存在的唯一标志。

 

进程由三部分组成:

1、程序:我们编写的程序用来描述进程要完成哪些功能以及如何完成

2、数据集:数据集则是程序在执行过程中所需要使用的资源

3、进程控制块:进程控制块用来记录进程的外部特征,描述进程的执行变化过程,系统可以利用它来控制和管理进程,它是系统感

     知进程存在的唯一标志。

线程

线程的出现是为了降低上下文切换的消耗,提高系统的并发性,并突破一个进程只能干一样事的缺陷,使到进程内并发成为可能。线程也叫轻量级进程,它是一个基本的CPU执行单元,也是程序执行过程中的最小单元,由线程ID、程序计数器、寄存器集合和堆栈共同组成。线程的引入减小了程序并发执行时的开销,提高了操作系统的并发性能。线程没有自己的系统资源。

Threading用于提供线程相关的操作。线程是应用程序中工作的最小单元,它被包含在进程之中,是进程中的实际运作单位。一

条线程指的是进程中一个单一顺序的控制流,一个进程中可以并发多个线程,每条线程并行执行不同的任务。

 

 

 

进程与线程的关系

进程是计算机中的程序关于某数据集合上的一次运行活动,是系统进行资源分配和调度的基本单位,是操作系统结构的基础。或者说进程是具有一定独立功能的程序关于某个数据集合上的一次运行活动,进程是系统进行资源分配和调度的一个独立单位。
线程则是进程的一个实体,是CPU调度和分派的基本单位,它是比进程更小的能独立运行的基本单位。

进程和线程的关系:

(1)一个线程只能属于一个进程,而一个进程可以有多个线程,但至少有一个线程。
(2)资源分配给进程,同一进程的所有线程共享该进程的所有资源。
(3)CPU分给线程,即真正在CPU上运行的是线程。

 进程: 资源管理单位(容器)

 线程: 最小执行单位

 

 并行与并发

 

并行处理(Parallel Processing)是计算机系统中能同时执行两个或更多个处理的一种计算方法。并行处理可同时工作于同一程序的不同方面。并行处理的主要目的是节省大型和复杂问题的解决时间。并发处理(concurrency Processing):指一个时间段中有几个程序都处于已启动运行到运行完毕之间,且这几个程序都是在同一个处理机(CPU)上运行,但任一个时刻点上只有一个程序在处理机(CPU)上运行

并发的关键是你有处理多个任务的能力,不一定要同时。并行的关键是你有同时处理多个任务的能力。所以说,并行是并发的子集

 

 同步与异步

 在计算机领域,同步就是指一个进程在执行某个请求的时候,若该请求需要一段时间才能返回信息,那么这个进程将会一直等待下去,直到收到返回信息才继续执行下去;异步是指进程不需要一直等下去,而是继续执行下面的操作,不管其他进程的状态。当有消息返回时系统会通知进程进行处理,这样可以提高执行的效率。举个例子,打电话时就是同步通信,发短息时就是异步通信。

 

1. 实现线程并发

 示例1:

 1 #!/usr/bin/env python
 2 # -*- coding:utf-8 -*- 
 3
 4 
 5 import threading  #线程
 6 import time
 7 
 8 def Hi(num): #有一个参数
 9     print("hello %s" %num)
10     time.sleep(3)  
11 
12 if __name__ == \'__main__\':
13 
14     t1=threading.Thread(target=Hi,args=(10,))  #创建了一个线程对象t1,10做为一个参数,传给num
15     t1.start()
16 
17     t2=threading.Thread(target=Hi,args=(9,))   #创建了一个线程对象t2,9做为一个参数,传给num
18     t2.start()
19 
20     print("ending.........")  #主线程输出ending

执行结果:

1 hello 10    #子线程
2 hello 9     #子线程
3 ending.........   #主线程
4 #上面三个同时出来,再停顿三秒才结束
5 Process finished with exit code 0  #停顿3秒才结束

示例2:

 1 #!/usr/bin/env python
 2 # -*- coding:utf-8 -*- 
 3 
 4 
 5 import threading
 6 import time
 7 
 8 def music():
 9     print("begin to listen %s"%time.ctime())
10     time.sleep(3)
11     print("stop to listen %s" %time.ctime())
12 
13 def game():
14     print("begin to play game %s"%time.ctime())
15     time.sleep(5)
16     print("stop to play game %s" %time.ctime())
17 
18 if __name__ == \'__main__\':
19 
20     t1=threading.Thread(target=music)
21     t1.start()
22     t2=threading.Thread(target=game)
23     t2.start()

 

执行结果:

1 #总共花了5秒时间
2 
3 begin to listen Sat Jan 14 12:34:43 2017
4 begin to play game Sat Jan 14 12:34:43 2017  #1、先打印2个
5 
6 stop to listen Sat Jan 14 12:34:46 2017      #2、等待3秒再打印一个
7 
8 stop to play game Sat Jan 14 12:34:48 2017   #3、再等待2秒,打印一个

 

 2.使用 join方法

示例1:

1 #!/usr/bin/env python
 2 # -*- coding:utf-8 -*- 
 3 
 4 
 5 import threading
 6 import time
 7 
 8 def music():
 9     print("begin to listen %s"%time.ctime())
10     time.sleep(3)
11     print("stop to listen %s" %time.ctime())
12 
13 def game():
14     print("begin to play game %s"%time.ctime())
15     time.sleep(5)
16     print("stop to play game %s" %time.ctime())
17 
18 if __name__ == \'__main__\':
19 
20     t1=threading.Thread(target=music)
21     t2=threading.Thread(target=game)
22 
23     t1.start()  #运行实例的方法
24     t2.start()
25 
26     t1.join()   #子线程对象调用join()方法
27     t2.join()
28 
29     print("ending")  #在主线程中

 

 执行结果:

1 begin to listen Sat Jan 14 12:58:34 2017
2 begin to play game Sat Jan 14 12:58:34 2017  #先打印2个
3 
4 stop to listen Sat Jan 14 12:58:37 2017      #等待3秒,再打印一个
5 
6 stop to play game Sat Jan 14 12:58:39 2017   #等待2秒,再打印两个
7 ending  

示例2:

 1 #!/usr/bin/env python
 2 # -*- coding:utf-8 -*- 
 3 
 4 
 5 import threading
 6 import time
 7 
 8 def music():
 9     print("begin to listen %s"%time.ctime())
10     time.sleep(3)
11     print("stop to listen %s" %time.ctime())
12 
13 def game():
14     print("begin to play game %s"%time.ctime())
15     time.sleep(5)
16     print("stop to play game %s" %time.ctime())
17 
18 if __name__ == \'__main__\':
19 
20     t1=threading.Thread(target=music)
21     t2=threading.Thread(target=game)
22 
23     t1.start()  #运行实例的方法
24     t2.start()
25 
26     t1.join()   #t1线程不结束,谁都不往下走
27 
28     print("ending")  

执行结果:

1 begin to listen Sat Jan 14 13:06:07 2017
2 begin to play game Sat Jan 14 13:06:07 2017  #先打印这两行
3 
4 stop to listen Sat Jan 14 13:06:10 2017      #再等待3秒打印这两行
5 ending
6 
7 stop to play game Sat Jan 14 13:06:12 2017   #再等待2秒打印这行

示例3:

 1 #!/usr/bin/env python
 2 # -*- coding:utf-8 -*- 
 3 
 4 
 5 import threading
 6 import time
 7 
 8 def music():
 9     print("begin to listen %s"%time.ctime())
10     time.sleep(3)
11     print("stop to listen %s" %time.ctime())
12 
13 def game():
14     print("begin to play game %s"%time.ctime())
15     time.sleep(5)
16     print("stop to play game %s" %time.ctime())
17 
18 if __name__ == \'__main__\':
19 
20     t1=threading.Thread(target=music)
21     t2=threading.Thread(target=game)
22 
23     t1.start()  #运行实例的方法
24     t2.start()
25 
26     t2.join()
27 
28     print("ending")  #在主线程中

执行结果:

1 begin to listen Sat Jan 14 13:12:34 2017     #先打印这两行
2 begin to play game Sat Jan 14 13:12:34 2017
3 
4 stop to listen Sat Jan 14 13:12:37 2017      #等待3秒,打印这一行
5 
6 stop to play game Sat Jan 14 13:12:39 2017   #等待2秒,打印这两行
7 ending

示例4: 并没有实现并发(实现多线程的意义)

 

 1 #!/usr/bin/env python
 2 # -*- coding:utf-8 -*- 
 3 
 4 
 5 import threading
 6 import time
 7 
 8 def music():
 9     print("begin to listen %s"%time.ctime())
10     time.sleep(3)
11     print("stop to listen %s" %time.ctime())
12 
13 def game():
14     print("begin to play game %s"%time.ctime())
15     time.sleep(5)
16     print("stop to play game %s" %time.ctime())
17 
18 if __name__ == \'__main__\':
19 
20     t1=threading.Thread(target=music)
21     t2=threading.Thread(target=game)
22 
23     t1.start()
24 
25     t1.join()
26     t2.start()
27     
28     t2.join()
29 
30     print("ending")  #在主线程中

执行结果:

1 begin to listen Sat Jan 14 13:26:18 2017    #先打印条1行
2 
3 stop to listen Sat Jan 14 13:26:21 2017     #等待3秒再打印2行
4 begin to play game Sat Jan 14 13:26:21 2017
5 
6 stop to play game Sat Jan 14 13:26:26 2017  #等待5秒打印2行
7 ending

  

 线程调用方法:

1.直接调用

 1 #!/usr/bin/env python
 2 # -*- coding:utf-8 -*- 
 3 
 4 
 5 import threading
 6 import time
 7 
 8 
 9 def sayhi(num):  # 定义每个线程要运行的函数
10 
11     print("running on number:%s" % num)
12 
13     time.sleep(3)
14 
15 
16 if __name__ == \'__main__\':
17     t1 = threading.Thread(target=sayhi, args=(1,))  # 生成一个线程实例
18     t2 = threading.Thread(target=sayhi, args=(2,))  # 生成另一个线程实例
19 
20     t1.start()  # 启动线程
21     t2.start()  # 启动另一个线程
22 
23     print(t1.getName())  # 获取线程名
24     print(t2.getName())

执行结果:

1 running on number:1
2 running on number:2
3 Thread-1
4 Thread-2

  

 2.继承式调用

 1 #!/usr/bin/env python
 2 # -*- coding:utf-8 -*- 
 3 
 4 
 5 import threading
 6 import time
 7 
 8 #自己定制一个MyThread的类
 9 class MyThread(threading.Thread):  
10     def __init__(self, num):
11         threading.Thread.__init__(self)
12         self.num = num
13 
14     def run(self):  # 定义每个线程要运行的函数
15 
16         print("running on number:%s" % self.num)
17 
18         time.sleep(3)
19 
20 
21 if __name__ == \'__main__\':
22     t1 = MyThread(1)  #继承这个类,把1这个参数,传给num ,t1就是个线程对象
23     t2 = MyThread(2)
24     t1.start()
25     t2.start()
26 
27     print("ending......")

执行结果:

1 running on number:1
2 running on number:2
3 ending......

  

 用Daemon方法示例(设置t为守护线程,就是字线程,跟着主线程一起推出)

   daemon: 程序直到不存在非守护线程时退出。

 1 #!/usr/bin/env python
 2 # -*- coding:utf-8 -*-
 3 
 4 
 5 import threading
 6 from time import ctime,sleep
 7 import time
 8 
 9 def ListenMusic(name):
10 
11         print ("Begin listening to %s. %s" %(name,ctime()))
12         sleep(3)
13         print("end listening %s"%ctime())
14 
15 def RecordBlog(title):
16 
17         print ("Begin recording the %s! %s" %(title,ctime()))
18         sleep(5)
19         print(\'end recording %s\'%ctime())
20 
21 #创建一个列表,把t1和t2加到列表中去
22 threads = []
23 t1 = threading.Thread(target=ListenMusic,args=(\'水手\',))
24 t2 = threading.Thread(target=RecordBlog,args=(\'python线程\',))
25 threads.append(t1)
26 threads.append(t2)
27 
28 if __name__ == \'__main__\':
29 
30     for t in threads:
31         t.setDaemon(True) #设置t为守护线程; 注意:一定在start()之前设置,否则会报错
32 
33         t.start()
34 
35     print ("all over %s" %ctime())

 

 执行结果:

1 Begin listening to 水手. Sat Jan 14 13:51:30 2017    #三个同时打印出来
2 Begin recording the python线程! Sat Jan 14 13:51:30 2017
3 all over Sat Jan 14 13:51:30 2017

  

示例3: 设置t1为守护线程,没有意义,达不到效果,因为t2还会继续执行

 1 #!/usr/bin/env python
 2 # -*- coding:utf-8 -*-
 3 
 4 
 5 import threading
 6 from time import ctime,sleep
 7 import time
 8 
 9 def ListenMusic(name):
10 
11         print ("Begin listening to %s. %s" %(name,ctime()))
12         sleep(3)
13         print("end listening %s"%ctime())
14 
15 def RecordBlog(title):
16 
17         print ("Begin recording the %s! %s" %(title,ctime()))
18         sleep(5)
19         print(\'end recording %s\'%ctime())
20 
21 #创建一个列表,把t1和t2加到列表中去
22 threads = []
23 t1 = threading.Thread(target=ListenMusic,args=(\'水手\',))
24 t2 = threading.Thread(target=RecordBlog,args=(\'python线程\',))
25 threads.append(t1)
26 threads.append(t2)
27 
28 if __name__ == \'__main__\':
29 
30     t1.setDaemon(True)  #设置t1为守护线程; 注意:一定在start之前设置,否则会报错
31     for t in threads:
32 
33         t.start()
34 
35     print ("all over %s" %ctime())

 

执行结果:

1 Begin listening to 水手. Sat Jan 14 14:02:07 2017
2 Begin recording the python线程! Sat Jan 14 14:02:07 2017
3 all over Sat Jan 14 14:02:07 2017          #设置t1为守护线程,所以会先把这三条先打印出来
4 
5 end listening Sat Jan 14 14:02:10 2017     #再等待3秒打印t2,
6 
7 end recording Sat Jan 14 14:02:12 2017     #再等待3秒打印这条出来

 

示例4:设置t2为守护线程,子线程才会跟着主线程一起退出

 1 #!/usr/bin/env python
 2 # -*- coding:utf-8 -*-
 3
 4 
 5 import threading
 6 from time import ctime,sleep
 7 import time
 8 
 9 def ListenMusic(name):
10 
11         print ("Begin listening to %s. %s" %(name,ctime()))
12         sleep(3)
13         print("end listening %s"%ctime())
14 
15 def RecordBlog(title):
16 
17         print ("Begin recording the %s! %s" %(title,ctime()))
18         sleep(5)
19         print(\'end recording %s\'%ctime())
20 
21 #创建一个列表,把t1和t2加到列表中去
22 threads = []
23 t1 = threading.Thread(target=ListenMusic,args=(\'水手\',))
24 t2 = threading.Thread(target=RecordBlog,args=(\'python线程\',))
25 threads.append(t1)
26 threads.append(t2)
27 
28 if __name__ == \'__main__\':
29 
30     t2.setDaemon(True)  # 设置t2为守护线程; 注意:一定在start之前设置,否则会报错
31     for t in threads:
32 
33         t.start()
34 
35     print ("all over %s" %ctime())

执行结果:

1 Begin listening to 水手. Sat Jan 14 14:17:09 2017
2 Begin recording the python线程! Sat Jan 14 14:17:09 2017
3 all over Sat Jan 14 14:17:09 2017       #先打印这三条
4 
5 end listening Sat Jan 14 14:17:12 2017  #等待3秒,再打印这条;t1结束后,主线程也结束了。

 

其他方法:

Thread实例对象的方法
# isAlive(): 返回线程是否活动的。 # getName(): 返回线程名。 # setName(): 设置线程名。 threading模块提供的一些方法: # threading.currentThread(): 返回当前的线程变量。 # threading.enumerate(): 返回一个包含正在运行的线程的list。正在运行指线程启动后、结束前,不包括启动前和终止后的线程。 # threading.activeCount(): 返回正在运行的线程数量,与len(threading.enumerate())有相同的结果。
 1 #!/usr/bin/env python
 2 # -*- coding:utf-8 -*-
 3 
 4 
 5 import threading
 6 from time import ctime,sleep
 7 import time
 8 
 9 def ListenMusic(name):
10 
11         print ("Begin listening to %s. %s" %(name,ctime()))
12         sleep(3)
13         print("end listening %s"%ctime())
14 
15 def RecordBlog(title):
16 
17         print ("Begin recording the %s! %s" %(title,ctime()))
18         sleep(5)
19         print(\'end recording %s\'%ctime())
20 
21 #创建一个列表,把t1和t2加到列表中去
22 threads = []
23 t1 = threading.Thread(target=ListenMusic,args=(\'水手\',))
24 t2 = threading.Thread(target=RecordBlog,args=(\'python线程\',))
25 threads.append(t1)
26 threads.append(t2)
27 
28 if __name__ == \'__main__\':
29 
30     t2.setDaemon(True)  # 设置t为守护进程; 注意:一定在start之前设置,否则会报错
31     for t in threads:
32         t.start()
33         print(t.getName())    #返回线程名称:Thread-1
34 
35     print ("all over %s" %ctime())

 

 

GIL(全局解释器锁)

无论你启多少个线程,你有多少个cpu, Python在执行的时候会淡定的在同一时刻只允许一个线程运行。

 

同步锁

 

1.不加锁(拿到的值是不固定的)

 1 import time
 2 import threading
 3 
 4 def addNum():
 5     global num #在每个线程中都获取这个全局变量
 6     #num-=1
 7 
 8     temp=num
 9     time.sleep(0.1)
10     num =temp-1  # 对此公共变量进行-1操作
11 
12 num = 100  #设定一个共享变量
13 
14 thread_list = []
15 
16 for i in range(100):
17     t = threading.Thread(target=addNum)
18     t.start()
19     thread_list.append(t)
20 
21 for t in thread_list: #等待所有线程执行完毕
22     t.join()
23 
24 print(\'Result: \', num)

2.加锁(互斥锁,就是把多线程变成串行,结果不会变)

 1 import time
 2 import threading
 3 
 4 def subNum():
 5     global num
 6     lock.acquire()
 7     temp=num
 8     time.sleep(0.01)
 9     num =temp-1  #对此公共变量进行-1操作
10     lock.release()
11 
12 num = 100  #设定一个共享变量
13 thread_list = []
14 lock=threading.Lock()
15 for i in range(100):
16     t = threading.Thread(target=subNum)
17     t.start()
18     thread_list.append(t)
19 
20 for t in thread_list: #等待所有线程执行完毕
21     t.join()
22 print(\'Result: \',num)

 

死锁和递归锁

 

 所谓死锁: 是指两个或两个以上的进程或线程在执行过程中,因争夺资源而造成的一种互相等待的现象,若无外力作用,它们都将无法推进下去。此时称系统处于死锁状态或系统产生了死锁,这些永远在互相等待的进程称为死锁进程。

死锁:

 1 import threading
 2 import time
 3 
 4 mutexA = threading.Lock()
 5 mutexB = threading.Lock()
 6 
 7 class MyThread(threading.Thread):
 8 
 9     def __init__(self):
10         threading.Thread.__init__(self)
11 
12     def run(self):
13         self.fun1()
14         self.fun2()
15 
16     def fun1(self):
17 
18         mutexA.acquire()  # 如果锁被占用,则阻塞在这里,等待锁的释放
19 
20         print ("I am %s , get res: %s---%s" %(self.name, "ResA",time.time()))
21 
22         mutexB.acquire()
23         print ("I am %s , get res: %s---%s" %(self.name, "ResB",time.time()))
24         mutexB.release()
25         mutexA.release()
26 
27 
28     def fun2(self):
29 
30         mutexB.acquire()
31         print ("I am %s , get res: %s---%s" %(self.name, "ResB",time.time()))
32         time.sleep(0.2)
33 
34         mutexA.acquire()
35         print ("I am %s , get res: %s---%s" %(self.name, "ResA",time.time()))
36         mutexA.release()
37 
38         mutexB.release()
39 
40 if __name__ == "__main__":
41 
42     print("start---------------------------%s"%time.time())
43 
44     for i in range(0, 10):
45         my_thread = MyThread()
46         my_thread.start()

 

在Python中为了支持在同一线程中多次请求同一资源,python提供了可重入锁RLock。这个RLock内部维护着一个Lock和一个counter变量,counter记录了acquire的次数,从而使得资源可以被多次require。直到一个线程所有的acquire都被release

以上是关于进程与线程的主要内容,如果未能解决你的问题,请参考以下文章

八.多进程与多线程

多线程编程

[转帖]Linux系统进程的知识总结,进程与线程之间的纠葛...

线程浅析

LINUX操作系统知识:进程与线程详解

Linux系统进程的知识总结,进程与线程之间的纠葛...