Python学习-线程

1.方法

run(): 用以表示线程活动的方法。
start():启动线程活动。
join([time]): 等待至线程中止。这阻塞调用线程直至线程的join() 方法被调用中止-正常退出或者抛出未处理的异常-或者是可选的超时发生。
isAlive(): 返回线程是否活动的。
getName(): 返回线程名。
setName(): 设置线程名。

2.创建线程的两种方式

继承式
import threading # 导入库
class Thread_ (threading.Thread):
    def __init__(self, n):
        threading.Thread.__init__(self)  #重写父类方法
        self.num = n

    def run(self):
    	print(self.num)
if __name__ == "__main__":
    a = Thread_(2)  # 开启一个线程
    a.start()       # 启动线程
基本的线程启用,导入threading,继承 threading.Thread类,重写父类方法,必须方法为run(),在外部调用则需要则需要进行实例化,调用start()启动,而不是run()
面向对象式
def Test(i):
    print('开始测试%d'%i)
    time.sleep(1)
    print('测试结束')

if __name__=='__main__':
  #多线程
    for i in range(5):#利用循环创建5个线程
        t=threading.Thread(target=Test,args=(i,)) # 使用线程类驱动函数,args为参数,没有可以不写,但是这样并不能够对run方法自定义,有一定的局限性
      #启动线程
        t.start()

3.多线程

为了展示同一个程序是否使用多线程的效果,我们进行对比,

未使用多线程
import time
import random
import threading

lists = ['python', 'django', 'java',
         'flask', 'perl', 'C sharp', 'hadoop'
]

new_lists = []


def work():
    if len(lists) == 0:
        return
    data = random.choice(lists)
    lists.remove(data)
    new_data = '%s_new' % data
    new_lists.append(new_data)
    time.sleep(1)


if __name__ == '__main__':
    start = time.time()
    for i in range(len(lists)):
        work()
    print('old list:', lists)
    print('new list:', new_lists)
    print('time is %s' % (time.time() - start))
old list: [] new list: ['hadoop_new', 'java_new', 'perl_new', 'django_new', 'C sharp_new', 'flask_new', 'python_new'] time is 7.025599002838135
使用多线程
import time
import random
import threading

lists = ['python', 'django', 'java',
         'flask', 'perl', 'C sharp', 'hadoop'
]

new_lists = []
def work():
    if len(lists) == 0:
        return
    data = random.choice(lists)
    lists.remove(data)
    new_data = '%s_new' % data
    new_lists.append(new_data)
    time.sleep(1)


if __name__ == '__main__':
    start = time.time()
    # print('old list len:', len(lists))
    t_list = []
    for i in range(len(lists)):
        t = threading.Thread(target=work)
        t_list.append(t)
        t.start()
    for t in t_list:
        t.join()

    print('old list:', lists)
    print('new list:', new_lists)
    # print('new_list len', len(new_lists))
    print('time is %s' % (time.time() - start))
old list: [] new list: ['python_new', 'hadoop_new', 'java_new', 'django_new', 'flask_new', 'perl_new', 'C sharp_new'] time is 1.0028798580169678

4.线程同步

多线程实现同步有四种方式:
锁机制,信号量,条件判断和同步队列,这部分我主要写的是锁机制。
锁机制:如果多个线程共同对某个数据修改,则可能出现不可预料的结果,为了保证数据的正确性,需要对多个线程进行同步。
使用 Thread 对象的 Lock 和 Rlock 可以实现简单的线程同步,这两个对象都有 acquire 方法和 release 方法,对于那些需要每次只允许一个线程操作的数据,可以将其操作放到 acquire 和 release 方法之间。

4.1.普通锁同步

普通锁同步
class myThread(threading.Thread):
    def __init__(self, threadID, name, counter):
        threading.Thread.__init__(self)
        self.threadID = threadID
        self.name = name
        self.counter = counter
    def run(self):
        print("Start " + self.name)
      # 获得锁,成功获得锁定后返回True
      # 可选的timeout参数不填时将一直阻塞直到获得锁定
      # 否则超时后将返回False
        threadLock.acquire()
        print_time(self.name, self.counter, 5)
      # 释放锁
        threadLock.release()
def print_time(threadName, delay, counter):
    while counter:
        time.sleep(delay)
        print("%s: %s" %(threadName, time.ctime(time.time())))
        counter -= 1
    
threadLock = threading.Lock() 
threads = []
# 创建新线程
thread1 = myThread(1, "Thread_1", 1)
thread2 = myThread(2, "Thread_2", 2)
# 开启新线程
thread1.start()
thread2.start()
# 添加线程到线程列表
threads.append(thread1)
threads.append(thread2)
# 等待所有线程完成
for t in threads:
     t.join()
print("Exit")

Start Thread_1Start Thread_2 Thread_1: Thu Jul 14 11:27:49 2022 Thread_1: Thu Jul 14 11:27:50 2022 Thread_1: Thu Jul 14 11:27:51 2022 Thread_1: Thu Jul 14 11:27:52 2022 Thread_1: Thu Jul 14 11:27:53 2022 Thread_2: Thu Jul 14 11:27:55 2022 Thread_2: Thu Jul 14 11:27:57 2022 Thread_2: Thu Jul 14 11:27:59 2022 Thread_2: Thu Jul 14 11:28:01 2022 Thread_2: Thu Jul 14 11:28:03 2022 Exit

4.2.递归锁

RLock(可重入锁)是一个可以被同一个线程请求多次的同步指令。RLock使用了“拥有的线程”和“递归等级”的概念,处于锁定状态时,RLock被某个线程拥有。拥有RLock的线程可以再次调用acquire(),释放锁时需要调用release()相同次数。可以认为RLock包含一个锁定池和一个初始值为0的计数器,每次成功调用 acquire()/release(),计数器将+1/-1,为0时锁处于未锁定状态。
构造方法:mylock = Threading.RLock()

递归锁同步
import threading
mylock = threading.RLock()
num = 0
class Thread(threading.Thread):
    def __init__(self, name):
        threading.Thread.__init__(self)
        self.t_name = name
    def run(self):
        global num
        while True:
            mylock.acquire()
            print('\n%s locked, number: %d' % (self.t_name, num))
            if num >= 3:
                mylock.release()
                print('\n%s released, number: %d' % (self.t_name, num))
                break
            num += 1
            print('\n%s released, number: %d' % (self.t_name, num))
            mylock.release()
def test():
    thread1 = Thread('A')
    thread2 = Thread('B')
    thread1.start()
    thread2.start()
if __name__ == '__main__':
    test() 

A locked, number: 0
A released, number: 1
A locked, number: 1
A released, number: 2
A locked, number: 2
A released, number: 3
A locked, number: 3
A released, number: 3
B locked, number: 3
B released, number: 3

5.线程池

一个线程的运行时间可以分为3部分:线程的启动时间、线程体的运行时间和线程的销毁时间。在多线程处理的情景中,如果线程不能被重用,就意味着每次创建都需要经过启动、销毁和运行3个过程。这会增加系统相应的时间,降低了效率。使用线程池处理,处理当前任务之后并不销毁而是被安排处理下一个任务,能够避免多次创建线程,从而节省线程创建和销毁的开销,能带来更好的性能和系统稳定性。
方法:
futures.ThreadPoolExecutor:创建线程池
submit():往线程池中加任务
done():线程池中的某个线程是否完成了任务
result():获取当前线程执行任务的结果

普通线程池
# coding:utf-8

import time
import os
import threading
from concurrent.futures import ThreadPoolExecutor

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

if __name__ == '__main__': 
    t = ThreadPoolExecutor(2)
    for i in range(10):
        t.submit(work, (i, ))

(0,) (1,) (2,)(3,)

(4,)(5,)

(6,)(7,)

(8,)(9,)

带有同步锁的线程池
import time
import os
import threading

from concurrent.futures import ThreadPoolExecutor


lock = threading.Lock()


def work(i):
    lock.acquire()
    print(i)
    time.sleep(1)
    lock.release()
    # return 'result %s' % i


if __name__ == '__main__':
    t = ThreadPoolExecutor(2)
    for i in range(10):
        t.submit(work, (i, ))   

(0,) (1,) (2,) (3,) (4,) (5,) (6,) (7,) (8,) (9,)

6.线程守护

有一种线程,它是在后台运行的,它的任务是为其他线程提供服务,这种线程被称为“后台线程(Daemon Thread)”,又称为“守护线程”或“精灵线程”。Python 解释器的垃圾回收线程就是典型的后台线程。如果所有的前台线程都死亡了,那么后台线程会自动死亡。

线程守护
def action(max):
    for i in range(max):
        print(threading.current_thread().name + "  " + str(i))
t = threading.Thread(target=action, args=(10,), name='后台线程')
# 将此线程设置成后台线程
# 也可在创建Thread对象时通过daemon参数将其设为后台线程
t.setDaemon = True # 这一步很关键,必须在start()之前设置,否则报错
# 启动后台线程
t.start()
for i in range(10):
    print(threading.current_thread().name + "  " + str(i))
后台线程 0 MainThread 0 后台线程 1 后台线程 2 MainThread 1 MainThread 2 MainThread 3 MainThread 4 MainThread 5 MainThread 6 MainThread 7 MainThread 8 后台线程 3 MainThread 9 后台线程 4 后台线程 5 后台线程 6 后台线程 7 后台线程 8 后台线程 9
posted @ 2022-07-14 18:14  Ancientswar  阅读(56)  评论(0)    收藏  举报