跳转至

Python-threading:线程

线程

  • 全局解释器锁(GTL):python代码的执行是由python虚拟机进行控制, 在主循环中只能有一个控制线程在执行
  • 一个进程的独立运行片段,一个进程里面可以有多个线程
  • 多线程之间的执行顺序是无序的
  • 一个进程的多个线程间共享数据,和上下文运行环境
  • 线程随着时间线轮转

多线程

  • 启动多线程后本体程序作为主线程存在
  • 每个子线程都是独立的个体 相互跟其他子线程平行
  • 主线程可以先运行完毕,等待子线程结束后 清理内存 等待时间不影响子线程的运行
  • 子线程使用join()来阻塞主线程(这样跟单线程有啥区别)
  • 多线程的全局变量是共享的会出现资源抢夺问题
  • 解决资源抢夺需要上互斥锁

单线程执行顺序

  • 如果没有f1.join()则主程序会顺序执行 不等子线程结束就继续执行 但是主线程执行执行完毕后会等待子线程结束后给子线程清理内存, 等待时间不影响子线程的正常运行
import threading
import time


def func_1(name):
    print('我叫{0}现在{1},我开始了'.format(name, time.ctime()))
    time.sleep(2)
    print('我叫{0}现在{1},我休息了2s 我结束了'.format(name, time.ctime()))


def func_2(name, age):
    print('我叫{0},今年{1},现在{2}我开始了'.format(name, age, time.ctime()))
    time.sleep(4)
    print('我叫{0},今年{1},现在{2}我休息了4s 我结束了'.format(name, age, time.ctime()))


def main():
    print('开始下发任务')
    f1 = threading.Thread(target=func_1,args=('小明',))
    f1.start()

    f2 = threading.Thread(target=func_2,args=('花花', 20))
    f2.start()

    print('主线程任务完成')


if __name__ == '__main__':
    main()
    print('这里主程序继续往下执行 不等子线程结束')

多线程的执行顺序

  • 没有规律!没有顺序,由系统调度 threading.Thread可以实例化一个对象 不过需要重写run方法
import threading
import time


def func_1(name):
    print('我叫{0}现在{1},我开始了'.format(name, time.ctime()))
    time.sleep(4)
    print('我叫{0}现在{1},我休息了4s 我结束了'.format(name, time.ctime()))


def func_2(name, age):
    print('我叫{0},今年{1},现在{2}我开始了'.format(name, age, time.ctime()))
    time.sleep(2)
    print('我叫{0},今年{1},现在{2}我休息了2s 我结束了'.format(name, age, time.ctime()))


def main():
    print('开始下发任务')
    f1 = threading.Thread(target=func_1,args=('小明',))
    f1.start()
    f2 = threading.Thread(target=func_2,args=('花花', 20))
    f2.start()

    f1.join()
    f2.join()

    print('子线程都已经结束了我才会被执行')


if __name__ == '__main__':
    main()
    print('main函数执行之后我才会执行 ,因为main中被堵塞了')

多线程的全局变量

  • 是共享的 动一发而牵动全身
from threading import Thread
import time

g_num = 100

def func_1():
    global g_num
    for i in range(5): #每次循环过后进行加一 循环五次
        g_num+=1


def func_2():
    global g_num
    print('现在全局变量{0}被修改成了{1}'.format("g_num",g_num))



print('线程创建之前g_num的值为{0}'.format(g_num))
f1 = Thread(target=func_1)
f1.start()

time.sleep(1)  #阻塞1s后 再执行func2看看全局变量是否被修改

f2 = Thread(target=func_2)
f2.start()
  • 如果两个线程共用一个全局变量那么可能会出现一些问题(资源竞争)
import threading
import time


g_num = 0


def func_1():
    global g_num
    for i in range(1000000):
        g_num+=1
    print('\rfunc1中的for循环了100w次得到的是{0}'.format(g_num))


def func_2():
    time.sleep(1)  #可以看到这句话如果不注释掉 每次运行的结果都不一样,因为两个线程在争抢资源
    global g_num
    for i in range(1000000):
        g_num+=1
    print('\rfunc2中的for循环了100w次得到的是{0}'.format(g_num))



f1 = threading.Thread(target=func_1)
f1.start()



f2 = threading.Thread(target=func_2)
f2.start()


f2.join()
print('都加减完毕目前全局变量修改成了{0}'.format(g_num))
  • 如果需要解决资源竞争需要上互斥锁
import threading
import time

lk = threading.Lock()

num = 0


def func1():
    global num

    for i in range(1000000):
        lk.acquire()
        num += 1
        lk.release()


def func2():
    global num

    for i in range(1000000):
        lk.acquire()
        num += 1
        lk.release()


def main():
    t1 = time.time()
    f1 = threading.Thread(target=func1)
    f2 = threading.Thread(target=func2)
    f1.start()
    f2.start()

    f1.join()
    f2.join()
    t2 = time.time()
    print('加锁用时:',t2 - t1)



if __name__ == "__main__":
    main()

    t3 = time.time()
    num_a = 0
    for i in range(2000000):
        num_a += 1
    t4 = time.time()
    print('不加用时:', t4 - t3)
  • 给线程内的功能函数传可变数据类型(比如列表啥的) 线程之间也是共享的
import threading
import time

a = [1,2,3,4]

def func_1(lst):
    print('func_1接收到一个可变类型list{0}'.format(lst))
    lst.append(5)
    print('func_1增加一个元素5到列表中')


def func_2(lst):

    time.sleep(1)   # 停顿一秒来让func_1有时间执行
    print('func_2接收到一个全局变量数据',lst)



f1 = threading.Thread(target=func_1,args=(a,))
f1.start()


f2 = threading.Thread(target=func_2,args=(a,))
f2.start()

join

  • 加入 join()的子线程都运行完毕主线程才会继续往下执行
import threading
import time


def func_1(name):
    print('我叫{0}现在{1},我开始了'.format(name, time.ctime()))
    time.sleep(4)
    print('我叫{0}现在{1},我休息了4s 我结束了'.format(name, time.ctime()))


def func_2(name, age):
    print('我叫{0},今年{1},现在{2}我开始了'.format(name, age, time.ctime()))
    time.sleep(2)
    print('我叫{0},今年{1},现在{2}我休息了2s 我结束了'.format(name, age, time.ctime()))


def main():
    print('开始下发任务')
    f1 = threading.Thread(target=func_1,args=('小明',))
    f1.start()
    f2 = threading.Thread(target=func_2,args=('花花', 20))
    f2.start()

    f1.join()
    f2.join()

    print('子线程都已经结束了我才会被执行')


if __name__ == '__main__':
    main()
    print('main函数执行之后我才会执行 ,因为main中被堵塞了')

daemon

  • 如果程序中将子程序设置成守护线程则子线程会在主进程结束时自动退出
  • 一般守护线程不那么重要 或者 此守护线程不允许离开主进程独立运行
  • 守护线程的声明应该设置在线程运行之前
import threading
import time

# '''
#         下面这段代码,主进程结束后 子线程依然会继续运行
# '''
# def func_1():
#     print('我开始了')
#     time.sleep(3)
#     print('我结束了')
#
# f1 = threading.Thread(target=func_1,args=())
# f1.start()
# time.sleep(1)
# print('主进程结束了哦')


'''
        试着加入daemon  子线程和主进程一起终止
        设置方法:二选一
            f2.setDaemon(True)
            f2.daemon = True
'''

def func_2():
    print('我是守护线程 我开始了')
    time.sleep(3)
    print('我是守护线程我结束了')  #并不会出现 因为主进程结束了 此进程自动终止

f2 = threading.Thread(target=func_2,args=())
f2.setDaemon(True )
# f2.daemon=True

f2.start()

time.sleep(1)
print('我是主进程我结束了')

  • look 互斥锁解决线程重全局变量计算的问题,相同的数据需要使用同一把锁
  • 注意:! 加锁十分浪费系统资源
import threading
import time

lk = threading.Lock()

num = 0


def func1():
    global num

    for i in range(1000000):
        lk.acquire()
        num += 1
        lk.release()


def func2():
    global num

    for i in range(1000000):
        lk.acquire()
        num += 1
        lk.release()


def main():
    t1 = time.time()
    f1 = threading.Thread(target=func1)
    f2 = threading.Thread(target=func2)
    f1.start()
    f2.start()

    f1.join()
    f2.join()
    t2 = time.time()
    print('加锁用时:',t2 - t1)



if __name__ == "__main__":
    main()

    t3 = time.time()
    num_a = 0
    for i in range(2000000):
        num_a += 1
    t4 = time.time()
    print('不加用时:', t4 - t3)

互斥锁