Python学习笔记之多线程

一、什么是线程,什么是进程

       首先,线程和进程都是一个时间段的描述,是cpu工作时间段的描述。

       线程是程序执行流的最小单元,线程有就绪,阻塞和运行三种状态,就绪就是指线程具备所有的运行条件,逻辑上可以运行,只需等待cpu分配时间片即可,运行状态就是指线程占有cpu时间片,正在运行,阻塞是指线程在等待一个事件(如某个信号量),逻辑上不可执行,每一个程序至少有一个线程。线程是程序中一个单一的顺讯控制流程,进程内有一个相对独立的、可调度的执行单元,是系统独立调度和分派cpu的基本单位指令运行时的调度单位,在单个程序中同时运行多个线程完成不同的工左,称为多线程

      进程是cpu分配资源的最小单位,也有就绪,运行,阻塞三种状态。

二,python多线程

      1、使用_thread模块创建两个线程
import time
import _thread
def function_one():
    for i in range(5):
        time.sleep(1)
        print('function_one run')
def function_two():
    for i in range(5):
        time.sleep(1)
        print('function_two run')
def main():
    #创建两个线程
    try:
       thread1 = _thread.start_new_thread(function_one,())
       thread2 =_thread.start_new_thread(function_two,())
    except Exception as Erorr:
        print(Erorr)
    else:
        print('线程创建成功')
        time.sleep(10)
if __name__ == '__main__':
    main()
       2、使用threading模块创建两个线程
import time
import threading
def function(times):
    for i in range(6):
        time.sleep(1)
        print('function_%s run %s'%(times,i))
def main():
    #创建两个线程
    try:
        t1 = threading.Thread(target=function,args=('one',))
        t2 = threading.Thread(target=function,args=('two',))
    except Exception as Erorr:
        print(Erorr)
    else:
        print('线程创建成功')
        #启动两个线程
        t1.start()
        t2.start()
        t1.join(20) #主线程结束前,t1子线程还未结束,主线程最多再等待t1子线程20秒。
        t2.join() #主线程结束前,t2子线程还未结束,主线程一直得等到t2子线程结束。

if __name__ == '__main__':
    main()

 三,线程锁

       当我们使用多线程岁同一块内存空间进行反复操作时,可能会导致内存数据混乱,例如如下代码,我们总会将第50张票在两窗口进行两次售卖的情况       

#在一个公园有两个窗口,一天售卖50张票,使用两个线程来模拟这个情况
import threading
import time
ticket = 50
def ticket_sales(times):
    global ticket
    while ticket > 0:
        print('窗口%d售出第%d张票'%(times,ticket))
        time.sleep(0.1)
        ticket = ticket - 1
def main():
    #创建两个线程
    try:
        t1 = threading.Thread(target=ticket_sales,args=(1,))
        t2 = threading.Thread(target=ticket_sales,args=(2,))
    except Exception as Erorr:
        print(Erorr)
    else:
        print('线程创建成功')
        #启动两个线程
        t1.start()
        t2.start()
        t1.join()
        t2.join() #主线程结束前,t2子线程还未结束,主线程一直得等到t2子线程结束。
if __name__ == '__main__':
    main()
       我们对上面的ticket使用线程锁。
#在一个公园有两个窗口,一天售卖50张票,使用两个线程来模拟这个情况
import threading
import time
ticket = 50
lock = threading.Lock()  # 创建线程锁

def ticket_sales(times):
    global ticket
    while ticket > 0:
        lock.acquire()
        if ticket>0:
            print('窗口%d售出第%d张票'%(times,ticket))
            time.sleep(0.1)
            ticket = ticket-1
        lock.release()
        time.sleep(0.1)

def main():
    #创建两个线程
    try:
        t1 = threading.Thread(target=ticket_sales,args=(1,))
        t2 = threading.Thread(target=ticket_sales,args=(2,))

    except Exception as Erorr:
        print(Erorr)
    else:
        print('线程创建成功')
        #启动两个线程
        t1.start()
        t2.start()
        t1.join() 
        t2.join() #主线程结束前,t2子线程还未结束,主线程一直得等到t2子线程结束。
if __name__ == '__main__':
    main()

四、线程优先级队列

        生产者-消费者模型
        生产者,扫描某个目录下的所有文件,并将其路径放入长度为4的队列中
        消费者,对队列中的文件,按照先进先出的顺序进行拷贝到指定目录

import threading
import time
from queue import Queue
import os


def SearchFileAbsPath(dirname):
    dirname = os.path.abspath(dirname)  #
    filenames = list()
    for root, dirs, files in os.walk(dirname, topdown=False):  # 扫描一层目录
        for name in files:
            filenames.append(root + os.path.sep + name)  # 每一个文件的绝对路径放入列表
    return filenames

class Producer(threading.Thread):
    def __init__(self,filenames,queue):
        super(Producer,self).__init__()
        self.queu = queue
        self.filenames = filenames

    def run(self):
        while self.filenames:
            if self.queu.full():
                print('队列已满,生产者请等待!!!')
            else:
                file = self.filenames[0]
                with open(file,'r') as file_it:
                    file_content = file_it.read()
                    #队列中存放了文件绝对路径和文件对应的内容组成的字典
                self.queu.put({file:file_content})
                print(file,'文件内容入队成功')
            time.sleep(1)

class Consumer(threading.Thread):
    def __init__(self,dirname,filenames,queue):
        super(Consumer,self).__init__()
        self.dirname= dirname
        self.queu = queue
        self.filenames = filenames
    def run(self):
        while self.filenames:
            if self.queu.empty():
                print('队列空,消费者请等待!!!')
            else:
                file_dict = self.queu.get()
                for key,values in file_dict.items():
                    with open(self.dirname+os.path.sep+os.path.split(key)[1],'w') as file_it:
                        file_it.write(values)
                    print(key,'复制成功!!!')
                    self.filenames.pop(0)   #将已经完完成拷贝的文件从列表中删除,保证while循环正常退出
            time.sleep(1)
def main():
    que = Queue(4)
    filenames = SearchFileAbsPath('/root/PycharmProjects/day13')
    producer = Producer(filenames,que)
    consumer = Consumer('/root/Desktop/hello',filenames,que)
    producer.start()
    consumer.start()
    producer.join()
    consumer.join()

if __name__ == '__main__':
    main()

五、使用线程池

       我们在使用多线程处理多个任务时,占有时间最多的并不是,我们程序的实际执行时间,而是我们的线程之间的上线文切换时间,这样就导致我们多线程并没有达到预想的效果。我们使用线程池,可以并发的同事执行多个任务,免去了大量线程之间的上线下文切换,最重要的是,不需要线程线程每次都进行资源的申请与释放。      

# (python3.4以后的可以使用)
# *******************线程池的第一种方式***********************
from concurrent.futures import ThreadPoolExecutor
import time
def hello():
    print(time.ctime())
    time.sleep(1)
def main():
    pool = ThreadPoolExecutor(max_workers=3)
    pool.submit(hello)
    pool.submit(hello)
    pool.submit(hello)
    time.sleep(5)
if __name__ == '__main__':
        main()

输出:

Sat Jun 16 04:33:17 2018
Sat Jun 16 04:33:17 2018
Sat Jun 16 04:33:17 2018
       从输出我们可以看出,线程池中的三个线程完成了并发。

       我们对上面的消费者和生产者模型使用线程池。         

from concurrent.futures import ThreadPoolExecutor
import time
import os
def SearchFileAbsPath(dirname):
    dirname = os.path.abspath(dirname)  #
    filenames = list()
    for root, dirs, files in os.walk(dirname, topdown=False):  # 扫描一层目录
        for name in files:
            filenames.append(root + os.path.sep + name)  # 每一个文件的绝对路径放入列表
    return filenames

def cpyfile(dirname,filename):
    """
    :param dirname:   拷贝后的文件放在的位置
    :param filename: 存放文件绝对路径
    :return:
    """
    with open(filename, 'r') as file_it:
        file_content = file_it.read()
    with open(dirname + os.path.sep + os.path.split(filename)[1], 'w') as file_it:
        file_it.write(file_content)
        print(filename, '复制成功!!!')
    time.sleep(1)
def main():
    filenames = SearchFileAbsPath('/root/PycharmProjects/day13')
    pool = ThreadPoolExecutor(max_workers=2)
    for filename in filenames:
        pool.submit(cpyfile,'/root/Desktop/hello',filename)
if __name__ == '__main__':
    main()


猜你喜欢

转载自blog.csdn.net/m0_37717595/article/details/80682880