python 线程

OK  ,看完进程,接下来看线程,线程是干什么呢的,跟进程有什么区别?

进程有很多优点,它提供了多道编程,让我们感觉我们每个人都拥有自己的CPU和其他资源,可以提高计算机的利用率。很多人就不理解了,既然进程这么优秀,为什么还要线程呢?其实,仔细观察就会发现进程还是有很多缺陷的,主要体现在两点上:

    •   进程只能在一个时间干一件事,如果想同时干两件事或多件事,进程就无能为力了。

    •   进程在执行的过程中如果阻塞,例如等待输入,整个进程就会挂起,即使进程中有些工作不依赖于输入的数据,也将无法执行。

    如果这两个缺点理解比较困难的话,举个现实的例子也许你就清楚了:如果把我们上课的过程看成一个进程的话,那么我们要做的是耳朵听老师讲课,手上还要记笔记,脑子还要思考问题,这样才能高效的完成听课的任务。而如果只提供进程这个机制的话,上面这三件事将不能同时执行,同一时间只能做一件事,听的时候就不能记笔记,也不能用脑子思考,这是其一;如果老师在黑板上写演算过程,我们开始记笔记,而老师突然有一步推不下去了,阻塞住了,他在那边思考着,而我们呢,也不能干其他事,即使你想趁此时思考一下刚才没听懂的一个问题都不行,这是其二。

    现在你应该明白了进程的缺陷了,而解决的办法很简单,我们完全可以让听、写、思三个独立的过程,并行起来,这样很明显可以提高听课的效率。而实际的操作系统中,也同样引入了这种类似的机制——线程。

看见下边目录了吧,不少吧,没事,进程线程从程序方法上都是如出一辙的,只是字母不一样而已

只要进程学明白了,线程就很好理解

 

  1. 线程的创建的两种方式
  2. 查看线程的进程id
  3. 验证线程是数据共享的
  4. 多进程和多线程的效率对比
  5. 锁  同步  互斥锁
  6. 死锁现象
  7. 递归锁
  8. 多线程的程序不结束多进程的程序不结束的区别
  9. 守护线程
  10. 子进程不能input

一. 线程的创建的两种方式

记住线程的单词,跟导入的单词字母,跟进程不一样

上代码

from threading import Thread         #线程的导入
# from multiprocessing import Process     #进程的导入,看清除了哈

第一种方式
def   f1(n):
  print('%s号线程任务'%n)
def   f2(n):
  print('%s号线程任务'%n)
if __name__ == '__main__':
  t1 = Thread(target=f1,args=(1,))
  t2 = Thread(target=f2,args=(2,))
  t1.start()
  t2.start()
  print('主线程')


第二种创建方式
class  MyThread(Thread):
  def __init__(self,name):
  super(MyThread, self).__init__()
  super().__init__()
  self.name = name
  def   run(self):
    print('hello girl :' + self.name)
if __name__ == '__main__':
  t = MyThread('alex')
  t.start()
  print('主线程结束')

看吧基本上都一样,没太大区别,当然本质上是有区别的

 

二. 查看线程的进程id

import os
from threading import Thread
from multiprocessing import Process
def   f1(n):
  print('1号',os.getpid())                 #这个功能没啥意思,把单词记住
  print('%s号线程任务'%n)
def   f2(n):
  print('2号',os.getpid())
  print('%s号线程任务'%n)
if __name__ == '__main__':
  t1 = Thread(target=f1,args=(1,))
  t2 = Thread(target=f2,args=(2,))
  t1.start()
  t2.start()
  print('主线程',os.getpid())
  print('主线程')

 

三.验证线程是数据共享的

import  os
import  time
from  threading  import  Thread
# from multiprocessing import Process

#通过对全局变量的修改来验证线程之间是数据共享的,共享同一进程中的数据
num = 100
def   f1(n):
  time.sleep(3)
  global num
  num = 3
print('子线程的num',num)

if __name__ == '__main__':
  t = Thread(target=f1,args=(1,))
  t.start()
#等待子线程运行结束才继续往下执行
  t.join()

  print('主线程中的num:',num)

看吧,最后结果跟进程的不一样,(主线程,跟子线程是数据共享的)。

四.多进程和多线程的效率对比

import   time
from   threading   import   Thread
from   multiprocessing   import   Process

def   f1():
# time.sleep(1) #io密集型
# 计算型:
  n = 10
  for i in range(10000000):
    n = n + i

if __name__ == '__main__':
#查看一下20个线程执行20个任务的执行时间
  t_s_time = time.time()
  t_list = []
  for i in range(5):
    t = Thread(target=f1,)
    t.start()
    t_list.append(t)
[tt.join() for tt in t_list]
t_e_time = time.time()
t_dif_time = t_e_time - t_s_time
#查看一下20个进程执行同样的任务的执行时间
p_s_time = time.time()
p_list = []
for i in range(5):
  p = Process(target=f1,)
  p.start()
  p_list.append(p)
[pp.join() for pp in p_list]
p_e_time = time.time()
p_dif_time = p_e_time - p_s_time
print('多线程的执行时间:',t_dif_time)
print('多jincheng的执行时间:',p_dif_time)

结果就是线程的运行速度是要比进程快的,这就是为什么要用线程的原因

 

五. 锁  同步  互斥锁

from   multiprocessing   import   Queue

import  queue
import  time
from   threading   import  Lock,  Thread
num = 100
def  f1(loc):
  # num -= 1
  loc.acquire()
  global num
  # num -= 1
  tmp = num
  tmp -= 1
  time.sleep(0.001)
  num = tmp
  loc.release()
if __name__ == '__main__':
  t_loc = Lock()
  t_list = []
  for i in range(10):
    t = Thread(target=f1,args=(t_loc,))
    t.start()
    t_list.append(t)
[tt.join() for tt in t_list]
print('主线的num',num)

锁,把线程锁住,别乱套

六.死锁现象

进程也有死锁与递归锁,在进程那里忘记说了,放到这里一切说了额,进程的死锁和线程的是一样的,而且一般情况下进程之间是数据不共享的,不需要加锁,由于线程是对全局的数据共享的,所以对于全局的数据进行操作的时候,要加锁。

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

import   time
from   threading   import   Thread, Lock, RLock

def   f1(locA,locB):
  locA.acquire()                
  print('f1>>1号抢到了A锁')
  time.sleep(1)
  locB.acquire()
  print('f1>>1号抢到了B锁')
  locB.release()

  locA.release()
def f2(locA,locB):
  locB.acquire()

  print('f2>>2号抢到了B锁')

  locA.acquire()
  time.sleep(1)
  print('f2>>2号抢到了A锁')
  locA.release()

  locB.release()
if __name__ == '__main__':
  locA = Lock()
  locB = Lock()
  t1 = Thread(target=f1,args=(locA,locB))
  t2 = Thread(target=f2,args=(locA,locB))
  t1.start()
  t2.start()

最后结果就是

f1>>1号抢到了A锁
f2>>2号抢到了B锁

七.递归锁

解决方法,递归锁,在Python中为了支持在同一线程中多次请求同一资源,python提供了可重入锁RLock。

    这个RLock内部维护着一个Lock和一个counter变量,counter记录了acquire的次数,从而使得资源可以被多次require。直到一个线程所有的acquire都被release,其他的线程才能获得资源。上面的例子如果使用RLock代替Lock,则不会发生死锁:

 

impor   time
from   threading   import   Thread,  Lock,  RLock
def   f1(locA, locB):
  # print('xxxx')
  # time.sleep(0.1)
  locA.acquire()
  print('f1>>1号抢到了A锁')

  time.sleep(1)
  locB.acquire()
  print('f1>>1号抢到了B锁')
  locB.release()

  locA.release()
def  f2(locA, locB):
  print('22222')
  time.sleep(0.1)
  locB.acquire()
  print('f2>>2号抢到了B锁')
  locA.acquire()
  time.sleep(1)
  print('f2>>2号抢到了A锁')
  locA.release()
  locB.release()
if __name__ == '__main__':
  #locA =locB = Lock()
  #locA = Lock()
  #locB = Lock()
  locA = locB = RLock()        #递归锁,维护一个计数器,acquire一次就加1,release就减1
  t1 = Thread(target=f1, args=(locA, locB))
  t2 = Thread(target=f2, args=(locA, locB))
  t1.start()
  t2.start()

最后结果就是

f1>>1号抢到了A锁
22222              #主线程的快一点
f1>>1号抢到了B锁
f2>>2号抢到了B锁
f2>>2号抢到了A锁

 

八.多线程的程序不结束多进程的程序不结束的区别

import   time
from   threading   import   Thread
from   multiprocessing   import   Process

#守护进程:主进程代码执行运行结束,守护进程随之结束          #这两行字是重点,好好理解一下,敲一下

#守护线程:守护线程会等待所有非守护线程运行结束才结束    #代码,看一下结果,对比一下

def   f1():
  time.sleep(2)
  print('1号线程')

def   f2():
  time.sleep(3)
  print('2号线程')
if __name__ == '__main__':
  # t1 = Thread(target=f1,)
  # t2 = Thread(target=f2,)
  # t1.daemon = True
  # t2.daemon = True
  # t1.start()
  # t2.start()
  # print('主线程结束')
  t1 = Process(target=f1, )
  t2 = Process(target=f2, )
  # t1.daemon = True
  # # t2.daemon = True
  t1.start()
  t2.start()

  print('主进程结束')

上面两行字是重点

 

九.守护线程

import   time
from   threading   import   Thread
from   multiprocessing   import   Process

def   f1():
  time.sleep(2)
  print('1号线程')
def   f2():
  time.sleep(3)
  print('2号线程')

if __name__ == '__main__':
  t1 = Thread(target=f1,)
  t2 = Thread(target=f2,)
  # t1.daemon = True              #方法,这就是守护进程
  t2.daemon = True
  t1.start()
  t2.start()
  print('主线程结束')
  # t1 = Process(target=f1, )
  # t2 = Process(target=f2, )
  # t1.daemon = True
  # # t2.daemon = True
  # t1.start()
  # t2.start()
  #
  # print('主进程结束')

 

十.子进程不能input

from   threading   import   Thread
from   multiprocessing   import   Process

def   f1():
  name = input('请输入名字') #EOFError: EOF when reading a line
  #
  print(name)

# if __name__ == '__main__':
  # input('主进程输入')
  # p = Process(target=f1,)
  # p.start()
  #
  # print('主进程结束')

# if __name__ == '__main__':
  # input('主进程输入')
  p = Thread(target=f1,)
  p.start()

  print('主进程结束')

子进程里面不能input,会报错

子线程里面可以。

线程知识点方法有点多,但还是那句话,跟进程差不太多,好好练一下,敲一下代码,单词记住,加油吧。

 

posted on 2019-01-14 18:34  python李洋洋  阅读(98)  评论(0编辑  收藏  举报

导航