线程与子线程(python3入门)

一、线程的概述

进程的缺点:
    1. 非常消耗资源,所以每次'主线程...'会先与子进程之前进行打印,一次我            们的子进程不能无限的打开
    2. 如果开了过多的子进程,cpu在进程的模式下切来切去是非常耗费时间的

因此引入线程:
    线程的出现,实就是要解决上面的两个问题
    轻量级的进程====> 线程
线程VS进程:
    - 线程是一个轻量级的进程
    - 一个进程里面至少有一个线程, 可以有多个线程
    - 线程是具体干活的

线程的好处:
    - 线程开启速度快于进程
    - 线程之间的数据是共享的
    - CPU在线程之间的切换速度远快于进程

使用的场景(进程线程)
    线程: 有大量IO存在的时候,使用线程
    
    进程: 有密集计算的时候使用

同一个进城之间的数据在线程之间的是共享的

二、开启线程的两种方式

# 方式一:使用函数的方式开启
# from threading import Thread
# import time
#
# def task(arg):
#     print('%s is running...'% arg)
#     time.sleep(1)
#     print('%s is done...'% arg)
#
# if __name__ == '__main__':
#     t = Thread(target=task, args=('子线程',))
#     t.start()   #开启的瞬间就执行了子线程的所有内容
#
#     print('主线程..')
方式一(函数型)
# 方式二:类的方式开启
from threading import Thread
import time

class MyThread(Thread):
    def run(self):
        print('%s is running...'% self)
        time.sleep(1)
        print('%s is done...'% self)

if __name__ == '__main__':
        t = MyThread()
        t.start()
        print('主线程...')
方式二(类)

三、同一进程下,线程的数据是共享的

from threading import Thread

x = 100

def task():
    global x
    x = 0


if __name__ == '__main__':
    t = Thread(target=task)
    t.start()

    print(x)    #结果x=0, 数据是共享的
View Code

四、进程和线程的执行速度对比

from threading import Thread
from multiprocessing import Process
import time

def task(arg):
    print('%s is running...'% arg)
    time.sleep(1)
    print('%s is done...'% arg)

if __name__ == '__main__':
    p = Process(target=task, args=('子进程',))
    p.start()

    t = Thread(target=task, args=('子线程',))
    t.start()   #开启的瞬间就执行了子线程的所有内容

    print('主线程..')
线程的运行速度要远快于进程

五、线程的其他属性

# 例1:
# from threading import Thread
# import time
#
# def task():
#     print('xxxx')
#
# if __name__ == '__main__':
#     t = Thread(target=task)
#     t.start()
#     t.join()    #等待子线程执行完毕
#     print(t.is_alive()) #判断子线程是否存活
#     print('主线程...')


# 例2:计算10个子线程运行的时间总和
import time
from threading import Thread

def task():
    print('xxx')
    time.sleep(1)

if __name__ == '__main__':

    start = time.time()
    t_l = []

    for i in range(10):
        t = Thread(target=task)
        t_l.append(t)
        t.start()

    for t in t_l:
        t.join()

    print('子线程总运行时间为%s'%(time.time()-start))
    print(t_l)
join(),is_alive()

六、守护线程

# 守护线程例1:
# 结论:当其他非守护线程执行完毕时,开启守护线程的子线程也随之结束
from threading import Thread
from threading import current_thread
import time


def task():
    print('%s is running...'% current_thread().name)
    time.sleep(3)
    print('%s is done...'% current_thread().name)

if __name__ == '__main__':
    t = Thread(target=task, name='守护线程')
    t.daemon = True
    t.start()
    print('主线程...')
守护线程例1
# 守护线程例2:
# 结论:当其他非守护线程执行全部完毕时,开启守护线程的子线程才随之结束
from threading import Thread
import time

def foo():
    print(123)
    time.sleep(3)
    print('end123')

def bar():
    print(456)
    time.sleep(1)
    print('end456')

t1 = Thread(target=foo)
t2 = Thread(target=bar)

t2.daemon = True

t1.start()
t2.start()

print('主线程...')
守护线程例2
# 守护进程例1
# 结论:当某个子进程开启了守护进程时,当父进程执行完毕时,该子进程也随之结束
from multiprocessing import Process
import time


def foo():
    print(123)
    time.sleep(3)
    print('end123')

def bar():
    print(456)
    time.sleep(1)
    print('end456')

p1 = Process(target=foo)
p2 = Process(target=bar)

p1.daemon = True
# p2.daemon = True

p1.start()
p2.start()

print('主线程...')
守护进程例子(与守护线程进行比较)

七、线程的互斥锁

# 例1: 线程运行速度太快了,导致数据失真(因为100个线程同时去拿x的时候得到x的值均为100。。。所以导致该例子的结打印x的时候值为99)
# from threading import Thread
# import time
#
# x = 100
#
# def task():
#     global x
#     tmp = x
#     time.sleep(0.1)   #模拟网络延迟
#     x = tmp - 1
#
#
#
# if __name__ == '__main__':
#     t_l = []
#
#     for i in range(100):
#         t = Thread(target=task,)
#         t_l.append(t)
#         t.start()
#
#     for j in t_l:
#         j.join()
#
#     print(x)
没有开启线程互斥锁的案例(会引起数据错乱失真)
# 例2: 使用线程互斥锁后的优化效果
# 线程的互斥锁基本和进程的互斥锁效果一致,(而进程是from multiprocess import Lock)
from threading import Thread
from threading import Lock
import time


mutex = Lock()
x = 100

def task():
    global x

    mutex.acquire() #获得锁,加一把锁

    tmp = x
    # time.sleep(0.1)
    x = tmp - 1

    mutex.release() #释放锁



if __name__ == '__main__':
    t_l = []

    for i in range(100):
        t = Thread(target=task,)
        t_l.append(t)
        t.start()

    for j in t_l:
        j.join()

    print(x)
开启线程互斥锁后(数据的使用恢复秩序井然)

八、线程的死锁和递归锁

例1: 下列代码进入了一个递归死锁的状态
from threading import Thread
from threading import Lock
import time



mutexA = Lock()
mutexB = Lock()

class MyThread(Thread):

    def run(self):
        self.f1()
        self.f2()

    def f1(self):
        mutexA.acquire()
        print('%s 拿到了A锁'% self.name)

        mutexB.acquire()
        print('%s 拿到了B锁'% self.name)

        mutexA.release()
        mutexB.release()

    def f2(self):
        mutexB.acquire()
        print('%s 拿到了B锁'% self.name)
        time.sleep(1)

        mutexA.acquire()
        print('%s 拿到了A锁'% self.name)

        mutexB.release()
        mutexA.release()

if __name__ == '__main__':
    for i in range(10):
        t = MyThread()
        t.start()
未开启线程递归锁Rlock,代码进入递归死锁的状态
# 例:使用递归锁Rlock 来解决上例的问题
from threading import Thread
from threading import Lock
from threading import RLock
import time



obj = RLock()   #可以连续的acquire()


# mutexA = Lock()
# mutexB = Lock()

mutexA = obj
mutexB = obj


class MyThread(Thread):

    def run(self):
        self.f1()
        self.f2()

    def f1(self):
        mutexA.acquire()
        print('%s 拿到了A锁'% self.name)

        mutexB.acquire()
        print('%s 拿到了B锁'% self.name)

        mutexA.release()
        mutexB.release()

    def f2(self):
        mutexB.acquire()
        print('%s 拿到了B锁'% self.name)
        time.sleep(1)

        mutexA.acquire()
        print('%s 拿到了A锁'% self.name)

        mutexB.release()
        mutexA.release()

if __name__ == '__main__':
    for i in range(10):
        t = MyThread()
        t.start()
当开启线程递归锁Rlock后,代码继续运行下去了

九、线程的信号量

from threading import Thread
from threading import Semaphore
from threading import current_thread
import time
import random


sema = Semaphore(5) #表示每次里面只有5个坑位

def task():
    with sema:
        print('%s 正在上厕所' % current_thread().name)
        time.sleep(random.randint(1,3))

if __name__ == '__main__':
    for i in range(20):
        t = Thread(target=task,)
        t.start()
线程的信号量示例

十、线程池(py3中引入了新的整合包)

线程池的使用

import time
import os
from concurrent.futures import ThreadPoolExecutor   #引入线程池
from concurrent.futures import ProcessPoolExecutor  #引入进程池

# 进程池和线程池的使用方法一模一样
# 下例为线程池的示例

def task(i):
    print(i)
    time.sleep(1)


if __name__ == '__main__':
    '''
    建议开启线程池的个数 = cpu个数*5
    '''
    # print(os.cpu_count())   #4个
    t = ThreadPoolExecutor(20)  #在线程池子里面放了20个线程

    res_li = []

    for i in range(30):
        res = t.submit(task, i )  # 和start一致,就是开始执行的意思
        res_li.append(res)

    t.shutdown()    #合并了进程池中的close + join的工作,作用就是停止提交任务,并等待子线程执行完毕

    for res in res_li:
        res.result()    #类似appy_async中的get()
线程池(在这个包内,进程池的使用方法和进程池一致)
原文地址:https://www.cnblogs.com/lich1x/p/10359084.html