python-多线程-守护线程-线程互斥锁-信号量-守护进程的使用

一、多线程

  1. 什么是线程:程序的执行线路,相当于一条流水线,其包含了程序的具体执行步骤,如果我们把操作系统比喻为一个工厂,进程就是车间,线程就是流水线
  2. 线程和进程的关系:进程中包含了运行改程序需要所有资源,进程是一个资源单位,线程是CPU的最小执行单位,每一个进程一旦被创建就默认开启了一条线程,称之为主线程,一个进程可以包含多个线程,进程包含线程,而线程依赖进程。
  3. 为什么使用线程:是为了提高程序效率,为何不用多进程提高效率?是因为进程对操作系统的资源耗费非常高
  4. 线程是如何提高效率的:多线程可以使CPU在一个进程内进行切换,从而提高CPU占用率
  5. 如何使用:两种开启线程的方式
    1. 实例化Thread类
    2. 继承Thread类覆盖run方法
  6. 什么情况下应该开启多线程:
    1. 当程序中遇到IO的时候
    2. 当程序中是纯计算任务时,也无法提高效率
  7. 进程和线程的区别:
    1. 进程对于操作系统的资源耗费非常高,而线程相反非常低(比进程低10-100倍)
    2. 在同一个进程中,多个线程之间资源是共享的

开启线程的第一种方式:

from threading import Thread
from multiprocessing import Process

def task():
    print("threading running!")

t1 = Thread(target=task)
t1.start()
print("over") 
#threading running!
#over

开启线程的第二中方式:

from threading import Thread
from multiprocessing import Process

class MyThread(Thread):
    def run(self):
        print("子线程 running....")
        
MyThread().start()
print("over2")

二、线程对比进程

100个进程时间统计:

from multiprocessing import Process
import time

def task():
    pass

# 100个进程时间统计
if __name__ == '__main__':
    start = time.time()
    ps = []
    for i in range(100):
        p = Process(target=task)
        p.start()
        ps.append(p)
    for p in ps:
        p.join()
    print(time.time()-start) #时间为6.289931774139404

100个线程时间统计:

from threading import Thread
import time

def task():
    pass

start = time.time()
ts = []
for i in range(100):
    t = Thread(target=task)
    t.start()
    ts.append(t)
for t in ts:
    t.join()
print(time.time()-start) #0.013973474502563477

三、线程间资源共享

from threading import Thread

x = 100
def task():
    print('run....')
    global x
    x = 0

t = Thread(target=task)
t.start()
t.join()

print(x)
print('over')
#run....
#0
#over

四、守护线程

  1. 什么是守护线程:守护线程会在所有非守护线程结束后结束,如果把线程分为皇帝、太子、和皇后,皇后设置为守护线程,那么皇后线程会在太子和皇帝都死亡后死亡当所有非守护线程结束后,守护线程也跟着结束了。同一个进程可以有多个守护线程
from threading import Thread
import time

def task():
    print('子线程运行')
    time.sleep(1)
    print('子线程结束')
t = Thread(target=task)
t.setDaemon(True)
t.start()
print("over")
#子线程运行
#over

五、线程中常用属性

from threading import Thread,current_thread,enumerate,active_count
import os
def task():
    print('running...')
    print(os.getpid()) #获取线程pid
    print(current_thread()) #获取当前线程对象
    print(active_count()) #获取当前正在运行的线程个数,包括主线程

t1 = Thread(target=task)
t1.start()
print(t1.is_alive()) #线程是否存活
print(t1.isAlive()) #线程是否存活
print(t1.getName()) #线程的名字
#获取所有线程对象列表
print(enumerate()) #[<_MainThread(MainThread, started 47944)>, <Thread(Thread-1, started 49204)>]

六、线程互斥锁

  1. 什么时候用锁:当多个进程或多个线程需要同时修改同一份数据时,可能会造成数据的错乱,所以必须得加锁
import time
from threading import Thread,Lock

lock =Lock()
a = 100

def task():
    lock.acquire()
    global a
    temp = a - 1
    time.sleep(0.01)
    a = temp
    lock.release()
    
ts = []
for i in range(100):
    t = Thread(target=task)
    t.start()
    ts.append(t)

for t in ts:
    t.join()

print(a) #0

七、信号量

  1. 什么是信号量:其实也是一种锁,特点是可以设置一个数据可以被几个线程(进程)共享
  2. 与普通锁的区别:普通锁一旦加锁,则意味着这个数据在同一时间只能被一个线程使用。信号量可以让这个数据在同一时间只能被多个线程使用
  3. 使用场景:可以限制一个数据被同时访问的次数,保证程序正常运行
from threading import Semaphore,Thread,current_thread
import time,random

sem = Semaphore(3)

def task():
    sem.acquire()
    print("%s run..." % current_thread())
    time.sleep(3)
    sem.release()

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

八、守护进程的使用

生成者与消费者模型

import time, random
from multiprocessing import Process, JoinableQueue


def eat_hotdog(name, q):
    while True:
        res = q.get()
        print('%s吃了%s' % (name, res))
        time.sleep(random.randint(1, 2))
        q.task_done()


def make_hotdog(name, q):
    for i in range(1, 6):
        time.sleep(random.randint(1, 2))
        print("%s生产了第%s个热狗" % (name, i))
        res = "%s的%s个热狗" % (name, i)
        q.put(res)


if __name__ == '__main__':
    q = JoinableQueue()
    #生产者1
    c1 = Process(target=make_hotdog, args=('万达热狗店', q))
    c1.start()
    #生产者2
    c2 = Process(target=make_hotdog, args=('老男孩狗店', q))
    c2.start()
	#消费者
    p2 = Process(target=eat_hotdog, args=('思聪', q))
    p2.daemon = True
    p2.start()
	#首先保证生产者全部产完成
    c1.join()
    c2.join()
    #保证队列中的数据全部被处理了
    q.join() #明确生产方已经不会再生成数据了

练习:

"""
1.基于多线程+TCP协议实现上传下载功能
多个人可以同时上传和下载,如果一个客户端相应同时并发的上传或下载多个文件 则必须为每一个上传/下载任务,建立新的连接,不能共享同一个,会造成数据错乱
2.编写一个文本处理工具,总共三个任务,一个接收用户输入,一个将用户输入的内容格式化成大写,一个将格式化后的结果存入文件
"""

上传下载服务端:

from threading import Thread
from concurrent.futures import ThreadPoolExecutor
import socket,json,struct,os
import common

server = socket.socket()
server.bind(("127.0.0.1",8989))
server.listen()

pool = ThreadPoolExecutor()

def recv_file(c):
    common.recv_file(c)

def send_file(c):
    common.send_file(c,r"D:\Program Files\Python\zdc\day35\多线程服务端.py","aaa.txt")


def task(c):
    print("执行了 task....")
    while True:
        # 接收用户数据 判断用户要执行的操作
        try:
            len_data = c.recv(4)
            if not len_data:
                c.close()
                break
            head_len = struct.unpack("i",len_data)[0]

            head = json.loads(c.recv(head_len).decode("utf-8"))
            print(head)
            print("服务器 收到一个请求")
            if head["func"] == "download":
                print("send....")
                pool.submit(send_file,c)
            elif head["func"] == "upload":
                recv_file(c)
            else:
                print("客户端请求错误!..")
        except ConnectionResetError:
            c.close()
            break

while True:
    client,addr = server.accept()
    pool.submit(task,client)

上传下载客户端:

import socket,json,struct,os
import common
from threading import Thread


is_uploading = False
is_downloading = False


c = socket.socket()
c.connect(("127.0.0.1",8989))

def download():
    global is_downloading
    if is_downloading:
        print("您有一个任务正在下载.....")
        return

    is_downloading = True
    print("11111")
    head = {"func":"download"}
    head_data = json.dumps(head).encode("utf-8")
    c.send(struct.pack("i",len(head_data)))
    c.send(head_data)
    common.recv_file(c)

    is_downloading = False


def upload():
    global is_uploading
    if is_uploading:
        print("您有一个任务正在上传.....")
        return
    is_uploading = True


    head = {"func": "upload"}
    head_data = json.dumps(head).encode("utf-8")
    c.send(struct.pack("i", len(head_data)))
    c.send(head_data)

    common.send_file(c,r"D:\Program Files\Python\zdc\day35\多线程客户端.py","nb.txt")

    is_uploading = False

funcs = {"1":download,"2":upload}
while True:
    res = input("请输入 1:下载 2:上传 q:退出").strip()
    if res == "q":
        break
    if res not in funcs:
        print("输入有误!")
        continue

    Thread(target=funcs[res]).start()# 开启线程执行上传或下载
==========================common.py==========================

import os,json,struct
import time

def send_file(c,filepath,filename):
    print("发送数据")
    f = open(filepath, "rb")

    file_info = {"name": filename, "size": os.path.getsize(filepath)}
    head_data = json.dumps(file_info).encode("utf-8")
    c.send(struct.pack("i", len(head_data)))
    c.send(head_data)

    time.sleep(5)
    # 发送文件
    while True:
        data = f.read(1024)
        if not data:
            break
        c.send(data)
    print("发送完成!")
    f.close()

def recv_file(c):
    print("接收数据")
    # 接收数据
    len_data = c.recv(4)
    head_len = struct.unpack("i", len_data)[0]

    file_info = json.loads(c.recv(head_len).decode("utf-8"))
    size = file_info["size"]
    recv_size = 0

    f = open(file_info["name"], "wb")
    while recv_size < size:
        data = c.recv(1024)
        f.write(data)
        recv_size += len(data)

    f.close()
    print("接收完成")

第二题:

from threading import Thread
import time

ls = []
up_ls = []
def input_data():
    while True:
        data = input(">>>:")
        ls.append(data)

def upper_data():
    while True:
        if ls:
            data = ls.pop(0).upper()
            up_ls.append(data)
        time.sleep(0.1)


def write_data():
    with open("test.txt","wt",encoding="utf-8") as f:
        while True:
            if up_ls:
                f.write(up_ls.pop(0))
                f.flush() # 立即将数据刷到硬盘
            time.sleep(0.1)


Thread(target=input_data).start()
Thread(target=upper_data).start()
Thread(target=write_data).start()
==========================================================
第二种方法:
from threading import Thread
from queue import Queue
def get_input(p):
    while True:
        msg = input(">>>").strip()
        p.put(msg)
        if msg == 'exit':break

def upper_msg(p,p2):
    while True:
        msg = p.get()
        new_msg = msg.upper()
        p2.put(new_msg)
        if msg == 'exit':break

def write_file(p2):
    while True:
        msg = p2.get()
        if msg == 'EXIT':break
        with open('test.txt','at',encoding='utf-8') as f:
            f.write(msg)
    print('done')

if __name__ == '__main__':
    q = Queue()
    q2 = Queue()
    t1 = Thread(target=get_input,args=(q,))
    t2 = Thread(target=upper_msg,args=(q,q2))
    t3 = Thread(target=write_file,args=(q2,))

    t1.start()
    t2.start()
    t3.start()

猜你喜欢

转载自blog.csdn.net/zdc45625/article/details/85632300