python-线程、线程锁

 

线程:

  线程是操作系统能够进行运算调度的最小单位,它被包含在进程中,是进程中的实际运作单位

  一个进程实际上可以由多个线程的执行单元组成。每个线程都运行在进程的上下文中,并共享同样的代码和全局数据。

  由于在实际的网络服务器中对并行的需求,线程成为越来越重要的编程模型,因为多线程之间比多进程之间更容易共享数据,同时线程一般比进程更高效

  线程可以理解成轻量的进程,实际在linux两者几乎没有区别,唯一的区别是线程并不产生新的地址空间和资源.

创建多线程有两种方式:

1、函数调用多线程

 

import threading
import time


def sayhi(num):  # 定义每个线程要运行的函数
    print("running on number:%s" % num)
    time.sleep(3)
def T_fun():
    t1 = threading.Thread(target=sayhi, args=(1,))  # 生成一个线程实例
    t2 = threading.Thread(target=sayhi, args=(2,))  # 生成另一个线程实例

    t1.start()  # 启动线程
    t2.start()  # 启动另一个线程

    print('t1 name =',t1.getName())  # 获取线程名
    print('t2 name =',t2.getName())

if __name__ == '__main__':
    T_fun()

>>>

   running on number:1
   running on number:2
   t1 name = Thread-1
   t2 name = Thread-2

 

2、继承调用多线程

 

 

import threading,time

class My_Threading(threading.Thread):
def __init__(self,num): super(My_Threading, self).__init__() self.num = num self.second = 0
def run(self):#定义每个线程所要执行的任务 print('start - %d - '%self.num) time.sleep(3) for i in range(5): t = My_Threading(i) t.start()
>>>
  start - 0 -   start - 1 -   start - 2 -   start - 3 -   start - 4 -

守护线程:

  当我们调用多线程进行操作时,主线程会等待子线程全部运行完成后再结束,如果想不管子线程是否执行完任务,如果主线程执行完成就结束任务的话,就需要到了守护线程。

import threading,time
def fun(num):

    time.sleep(1)
    print('num is ',num)


for i in range(5):
    t = threading.Thread(target=fun,args =(i,))
    t.setDaemon(True)  #一定要在start之前设置
    t.start()

print('run the end')

>>>
    run the end
#当主线程退出时,线程也会退出,由启动的其它子线程会同时退出,不管是否执行完任务
 

 

线程锁:

  在我们进行多线程任务时,需要对同一个数据进行更改,但是我们需要这些线程一个一个的对其进行更改,我们就需要用到了线程锁。

'''没用线程锁'''
def fun():
    global num
    num += 1
    # 完全计算密集型

for i in range(500):
    t = threading.Thread(target=fun)
    t.start()

print('num =',num)
>>>num = 500

def fun():
    global num  #1
    temp = num  #2
    time.sleep(0.001)  #I/O操作 #3
    num = temp+1 #4

for i in range(500):
    t = threading.Thread(target=fun)
    t.start()

print('num =',num)
>>>num = 32
>>>num = 31
# 执行过程:
'''
        在同一进程内,开启了500个线程,但因为GIL锁的原因(同一进程内,只有一个线程才可以被CPU调用(计算))
        1、当第一个线程(线程1)通过竞争获取到CPU执行权限后,会串行执行fun函数的 #1-#2,线程内的num的值为0
        2、当到达第 #3 的时候,是I/O操作,所以开始切换到第二个个线程上执行同样的 #1-#2的操作,这个线程内的bun值也为0,
        3、就这样碰到IO操作的时候就切换.... 
        4、但是当时间过了0.001秒的时候,[线程1]的线程I/O操作结束,执行 #4 将num的值更改为1
        5、线程结束,切换到其他线程,此时的num值为1,此时[线程2]的I/O操作也结束了,进行num更改(此时线程2内的值为0),更改后的值为还是1
        6、继续切换线程重新对num赋值,因为会发生重复操作,达不到我们想要的结果,所以就需要用到线程锁来保护我们的num
'''

在这里,通过线程锁来总结一下Python在解释器中放置的GIL锁的问题,先不管为什么放置GIL锁,这里只说一下放置GIL锁的后果是:

  one Python run, while N others sleep or await I/O.   

  在一个进程内,同一时刻只能有一个线程通过GIL锁 被CUP调用,切换条件:I/O操作、固定时间(系统决定)

  而且在python2里,启用多线程进行密集运算的时间要比串行执行运算的事件要长,这一点在python3中被优化了很多,几乎和串行执行的时间相同。

  更多

''''使用线程锁之后'''

import threading
lock = threading.RLock()
num = 0 #共享变量

def fun(): lock.acquire() global num num += 1 time.sleep(1) print('num =',num) lock.release() for i in range(5): t = threading.Thread(target=fun) t.start() >>> num = 1 num = 2 num = 3 num = 4 num = 5

  

 

Threading.Event:

  Event 是线程之间通信的机制之一,一个线程负责发送一个event 信号,其他线程则等待这个信号,用于主线程控制其他线程的执行。

Event 主要有四个方法:

   set() :设置标识位为 True

   clear() : 设置表示位为 False wait([timeout]): 起到阻塞作用,参数为阻塞时间,如设置为True则继续运行

   isSet(): 判断标识位是否设置为 True

import threading

def do_Something(event_Obj):
    print('start:')
    event_Obj.wait() #这里起到阻塞的作用设置标识为等待设置为True时 才继续执行,如果添加参数则是以float为参数,表示阻塞XX秒后继续执行程序
    print('execute!')

event_Obj = threading.Event() #Event 在初始化时,标识位为False

for i in range(5):
    t = threading.Thread(target=do_Something,args=(event_Obj,))
    t.start()


inp = input('>>>>')
if inp == 'T':
    event_Obj.set()

>>>
  

  start:
  start:
  start:
  start:
  start:
  >>>>T
  execute!
  execute!
  execute!
  execute!
  execute!

 

 

posted @ 2017-06-22 09:03  LeeeetMe  阅读(143)  评论(0编辑  收藏  举报