Python服务器运维笔记:第二章Linux - 1.2.10 多线程

本文介绍了Python中的多线程概念,包括进程、线程的基本理解,以及单线程问题和多线程的优势。详细讲解了`threading`模块,包括线程对象、线程锁、线程局部变量的使用,并通过示例代码展示了多线程的实现,强调了在多线程开发中防止变量干扰的方法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

前言:本文是学习网易微专业的《python全栈工程师》 中的《服务器运维开发工程师》专题的课程笔记,欢迎学习交流。同时感谢老师们的精彩传授!

一、课程目标
  • 多线程概念
  • 多线程实现
  • 多线程server socket
二、详情解读
2.1.什么是多线程

什么是进程?
我们每运行一个程序,就是创建一个进程。每个进程运行的时候会向系统申请资源:内存空间,cpu时间,进程之间的资源相互独立

什么是线程?
当一个进程运行的时候,它内部就会存在一个主线程。线程就是程序内部需要完成的任务。在只有一个线程的情况,这些任务需要按照次序,逐个完成。所有线程共享进程资源。
在这里插入图片描述
单线程的问题:
由于一个线程的情况,这些任务需要按照次序,逐个完成,所以当其中一个任务耗时比较长,那么其他任务就需要等待,这样就浪费了等待时间
在这里插入图片描述
多线程:
将进程中的多个任务独立开来运行,就是多线程。

第一次列表页面爬取后,提取内容列表任务变可以在第二次列表页面爬取期间完成,这样就会提高运行效率。

在这里插入图片描述

2.1.1.threading模块

多线程不代表一定同时执行。 多线程可以串行,也可以并行。
在这里插入图片描述

2.2.threading模块
from threading import Threading, active_count, current_thread, local
t = Thread(target=func, args=(param1, param2), kwargs={})
t.start() # 开始运行该线程
t.join()  # 其他线程必须等待此线程完成,才能运行

说明:
1).Thread 表示线程对象,通过该对象创建线程
2).t为线程实例
3).active_count - 函数,返回当前活动线程数
4).current_thread - 函数,当前该线程
5).local - 函数,线程局部变量
6).Lock - 锁对象

示例代码如下:

from threading import Thread
from random import randint
from time import sleep,time

def myprint(tid):
    sleep(randint(0,3))
    print("threading:",tid)
def main():
    for i in range(10):
        t = Thread(target=myprint,args=(i,))
        t.start()
        # t.join()       

if __name__ == "__main__":
    main()
    print("main stop....")

运行结果:
在这里插入图片描述
上面的程序,如果在t.start()后面添加t.join(),则每个线程都会等待前面的线程结束才会执行,结果如下图:
在这里插入图片描述

接着,测试多线程与非多线程的时间差别:

线程按照顺序执行:

# -*- coding=utf-8 -*-
from threading import Thread
from random import randint
from time import sleep, time

def myprint(tid):
    sleep(randint(0, 3))
    print('threading: ', tid)

def main():
    for i in range(10):
        t = Thread(target=myprint, args=(i, ))
        t.start()
        t.join()

if __name__ == '__main__':
    start_time = time()
    main()
    print('main stop...')
    end_time = time()
    print(end_time - start_time)

在这里插入图片描述
多线程的运行:

# -*- coding=utf-8 -*-
from threading import Thread
from random import randint
from time import sleep, time

def myprint(tid):
    sleep(randint(0, 3))
    print('threading: ', tid)

threads = []
def main():
    for i in range(10):
        # print('create', i)
        t = Thread(target=myprint, args=(i, ))
        t.start()
        threads.append(t)

if __name__ == '__main__':
    start_time = time()
    main()
    print('main stop...')
    # 检查每一个线程是否执行完毕
    for i in threads:
        i.join()
    end_time = time()
    print(end_time - start_time)

在这里插入图片描述
通过上面的测试,可以看出,多线程可以大大提高程序的执行效率。

消费者与生产者示例代码:

# -*- coding: utf-8 -*-

from threading import Thread, current_thread
from time import sleep
from random import randint
from queue import Queue

q = Queue(20)
def put_data():
    while True:
        n = randint(0,3)
        sleep(n)
        q.put(n)
        print(current_thread(), "向队列写入>>:",n)

def get_data():
    while True:
        data = q.get()
        print(current_thread(), "从队列读取>>", data)

def main():

    t1 = Thread(target=put_data)
    t2 = Thread(target=get_data)
    t1.start()
    t2.start()

if __name__ == "__main__":
    main()

local线程局部变量
线程局部对象可以被所有线程访问,比如local_data,可以被所有线程访问,但是线程局部对象的属性只能被定义该属性的线程自身访问,比如local_datadata属性,只能被自己的线程访问与修改,即使所有的线程中都包含同名的local_data.data属性,因此这个data实际上只是线程内部的局部变量,借助线性局部对象可以简化代码逻辑,保持线程间变量隔离。

示例代码:

# -*- coding=utf-8 -*-
from threading import Thread, current_thread, local
from random import randint
from time import  sleep

local_data = local()
data = 0
def set_data():

    local_data.data = randint(0,100)
    print(current_thread().name+"::data="+str(local_data.data))
    sleep(randint(0,3))
    print(current_thread().name+":: after_sleep::data="+str(local_data.data))
    # local_data.data = data
    out()

def out():
    print(local_data.data)

def main():
    for i in range(3):
        t1 = Thread(target=set_data)
        t1.start()

if __name__ == "__main__":
    main()

执行结果:
在这里插入图片描述

2.3.线程锁

所有线程可以共享一个进程内的资源,如果同时操作某一个对象,就可能发生混乱,这时候可以通过加锁解决。

线程锁通过Lock对象实例实现

l = Lock()

包含两个实例方法:

l.acquire()  # 加锁
l.release()  # 解锁

示例代码:

# -*- coding: utf-8 -*-

from threading import Thread, Lock, current_thread
from time import sleep
from random import randint

lock = Lock()
number = [0]

def loop(i):
    lock.acquire()
    try:
        if i > number[-1]:
            sleep(randint(0, 5))
            number.append(i)
    except:
        pass
    finally:
        print("thread %s ended." % current_thread().name, number, "\r\n")
        lock.release()
        pass


if __name__ == "__main__":

    for i in range(10):
        t = Thread(target=loop, args=(i,))
        t.start()

执行结果:
在这里插入图片描述

2.4.多线程socket_server实现
2.4.1.单线程socket_server问题

在这里插入图片描述
在这里插入图片描述
代码演示:

服务端代码:

from socket import socket, AF_INET, SOCK_STREAM
from threading import Thread

s_server = socket(AF_INET, SOCK_STREAM)
s_server.bind(("127.0.0.1", 8000))
s_server.listen()

def chat(client):
    while True:
        try:
            message = client.recv(1024)
        except:
            break
        else:
            print(message.decode("utf-8"))
            # 空字符串跳过
            if not message:
                continue
            if message == "quit":
                print("close")
                client.close()
                break
            else:
                print("send")
                client.send(b"\r\nrecived...")    


while True:
    print("wait......")
    s_client, addr = s_server.accept()
    t = Thread(target=chat, args=(s_client,))
    t.start()

客户端代码:

# -*- coding: utf-8 -*-
from socket import socket, AF_INET, SOCK_STREAM
from threading import Thread

s_client = socket(AF_INET, SOCK_STREAM)
s_client.connect(("127.0.0.1", 8000))


def chat():
    print("wait...")
    while True:
        message = input("请输入消息:")

        if not message:
            continue
        s_client.send(bytes(message, encoding="utf-8"))
        
        if message == "quit":
            break
    s_client.close()
    print("close()")

def get_message():
    while True:
        try:
            message = s_client.recv(1024)
            print(message.decode())
        except Exception as e:
            print(e)
            break
        finally:
            pass

t = Thread(target=chat)
t.start()
t = Thread(target=get_message)
t.start()
三、课程小结
  • 01 threading模块
  • 02 服务器端
  • 03 客户端

线程开发要注意变量之间的操作,防止变量互相干扰。
方法有两种:
1.如果变量只是对线程内部有效,可以使用线程局部变量。
2.如果变量需要多个线程共享共同操作,可以加锁

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值