• 并发编程之线程


    多进程

    核心--多道技术

    切换+保存状态

    程序I/O操作较多时,可提高程序效率

     

    多线程

    概念:线程是应用程序中工作的最小单元,或者又称之为微进程。 

    进程是一个资源单位,包含运行程序所需的所有资源,线程才是CPU上的执行单位

    每个进程一旦被创建,就默认开启了一条线程,其为主线程,有且只有一个。

    执行代码时,如遇I/O系统会自动切换到其他应用程序--降低了当前应用程序的效率。若进程中不仅有主线还有其他线程,主线遇到I/O时--切到其他线程,保证了应用程序对CPU利用率(占用率)

    对于CPU而言,只有线程可以被执行,所以CPU切换是在不同线程之间进行切换,开启多线程可提高效率---

    多线程即在一个进程中存在多个控制线程,多个控制线程共享该进程的地址空间

    创建进程的开销要远大于线程

    进程之间是竞争关系,线程之间是协作关系

    使用线程:提高效率

    为何不用多进程--进程对操作系统的资源耗费非常高

    线程在执行过程中与进程还是有区别的。每个独立的线程有一个程序运行的入口、顺序执行序列和程序的出口。但是线程不能够独立执行,必须依存在应用程序中,由应用程序提供多个线程执行控制。

    每个线程都有他自己的一组CPU寄存器,称为线程的上下文,该上下文反映了线程上次运行该线程的CPU寄存器的状态。

    指令指针和堆栈指针寄存器是线程上下文中两个最重要的寄存器,线程总是在进程得到上下文中运行的,这些地址都用于标志拥有线程的进程地址空间中的内存。

    • 线程可以被抢占(中断)。
    • 在其他线程正在运行时,线程可以暂时搁置(也称为睡眠) -- 这就是线程的退让。

    线程可以分为:

    • 内核线程:由操作系统内核创建和撤销。
    • 用户线程:不需要内核支持而在用户程序中实现的线程。

    使用:

    • threading(推荐使用)

    thread 模块已被废弃。用户可以使用 threading 模块代替。所以,在 Python3 中不能再使用"thread" 模块。为了兼容性,Python3 将 thread 重命名为 "_thread"。反正你不要再用就是了! 我习惯上用 from threading import Thread

    两种开启线程的方式

    1.实例化Thread

    2.继承Thread类,覆盖run方法

    # 开启进程第一种方式
    from threading import Thread
    
    def task():
        print("threading running")
    t1=Thread(target=task)
    t1.start()
    print("over")
    
    
    # 第二种方式
    class MyThread(Thread):
        def run(self):
            print("子线程 running...")
    
    MyThrestart()
    print("over2")

    当程序中遇到I/O时-------开启多线程,当程序中为纯计算任务时-----无法提高效率

    多线程与多进程区别: 

    1.多个线程共享创建它的进程的地址空间,资源共享;进程有自己的地址空间。
    2.线程可以直接访问其进程的数据段;进程有它们自己的父进程数据段的副本。
    3.线程可以直接与其进程的其他线程通信;进程必须使用进程间通信来与同级进程通信。 
    4.新线程很容易创建;新进程需要重复父进程。
    5.线程可以对同一进程的线程进行相当大的控制;进程只能对子进程进行控制。
    6.对主线程的更改(取消、优先级更改等)可能会影响进程的其他线程的行为;对父进程的更改不会影响子进程。
    进程对操作系统的资源耗费非常高,线程非常低
    进程数据相互隔离,同一进程中线程数据共享
    IO密集的程序 ---多线程---IO反正都要被阻塞,切换 线程的方式不会比进程慢多少
    计算密集的程序 ---多进程 ---利用多核可以达到并行!

    时间区别:

    # 开启进程第一种方式
    from threading import Thread
    
    def task():
        print("threading running")
    t1=Thread(target=task)
    t1.start()
    print("over")
    
    
    # 对比进程启动时间
    from multiprocessing import Process
    
    def task():
        print("running")
    
    if __name__ == '__main__':
        t2=Process(target=task)
        t2.start()
        print("over...")

    守护线程(setDaemon)

    在(同进程下)所有非守护线程结束后结束


    守护进程使用--生产者消费者模型

    one)
    
    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.start()
    
    
        # 首先保证生产者全部产完成
        c1.join()
        c2.join()
    
        # 保证队列中的数据全部被处理了
        q.join() # 明确生产方已经不会再生成数据了
    View Code

    Thread方法和一些常用属性

    threading 模块除了包含 _thread 模块中的所有方法外,还提供的其他方法:
    threading.currentThread(): 返回当前的线程变量。
    threading.enumerate(): 返回一个包含正在运行的线程的list。正在运行指线程启动后、结束前,不包括启动前和终止后的线程。
    threading.activeCount(): 返回正在运行的线程数量,与len(threading.enumerate())有相同的结果。
    
    除了使用方法外,线程模块同样提供了Thread类来处理线程,Thread类提供了以下方法:
    run(): 用以表示线程活动的方法。
    start():启动线程活动。 
    join([time]): 等待至线程中止。这阻塞调用线程直至线程的join() 方法被调用中止-正常退出或者抛出未处理的异常-或者是可选的超时发生。
    setDaemon(True):守护主线程,跟随主线程退(必须要放在start()上方)
    isAlive(): 返回线程是否活动的。
    getName(): 返回线程名。
    setName(): 设置线程名。
     
    View Code
    # 获取所有线程对象,返回一个列表
    print(enumerate())
    # 获取当前正在运行的线程个数
    print(active_count())
    # 获取当前线程对象
    print(current_thread())

    加锁

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

    互斥锁

    多个线程共同对某个数据修改,则可能出现不可预料的结果,为了保证数据的正确性,需要对多个线程进行同步。

    使用 Thread 对象的 Lock 和 Rlock 可以实现简单的线程互斥。

    这两个对象都有 acquire 方法和 release 方法。

    对于那些需要每次只允许一个线程操作的数据,可以将其操作放到 acquire 和 release 方法之间。

    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)
    View Code

    死锁---与进程无区别

    Rlock--可重用锁

    信号量 BoundedSemaphore或Semaphore

    其实也是一种互斥(锁),特点是可以设置一个数据可以被一个线程/进程共享,即限制线程的并发量

    与普通锁区别:

    普通锁一旦加锁,则这个数据同一时间只能被一个线程使用

    信号量 可以让这个数据在同一时间只能被多个线程使用

    使用场景:

    限制一个数据被同时的访问的次数,保证程序正常运行

    import time
    from threading import  Semaphore,Thread,current_thread
    
    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()
    练习:
        1、基于多线程实现并发的套接字通信
    ​    2、基于多线程+TCP协议实现上传下载功能
    ​    3、编写一个文本处理工具,总共三个任务,一个接收用户输入,一个将用户输入的内容格式化成大写,一个将格式化后的结果存入文件    

    练习

    1、基于多线程实现并发的套接字通信

    2、基于多线程+TCP协议实现上传下载功能

    3、编写一个文本处理工具,总共三个任务,一个接收用户输入,一个将用户输入的内容格式化成大写,一个将格式化后的结果存入文件

     

    import socket
    
    client = socket.socket()
    client.connect(("127.0.0.1", 9090))
    
    while True:
        msg = input(">>>:").strip()
        if not msg:
            continue
        client.send(msg.encode("utf-8"))
    
    
    
    import socket
    from threading import Thread
    
    server = socket.socket()
    
    server.bind(("127.0.0.1", 9090))
    server.listen(3)
    
    
    def task(client):
        while True:
            try:
                data = client.recv(1024)
                print(data.decode("utf-8"))
                client.send(data.upper())
    
            except Exception as e:
                print(e)
    
    
    if __name__ == '__main__':
        while True:
            client, addr = server.accept()
            t1 = Thread(target=task, args=(client,))
            t1.start()
            print("hi")
    1、基于多线程实现并发的套接字通信
    客户端:
    import json
    import socket
    import struct
    from threading import Thread
    
    import common
    
    is_uploading = False
    is_downloading = False
    
    c = socket.socket()
    c.connect(("127.0.0.1", 9090))
    
    
    def download():
        global is_downloading
        if is_downloading:
            print("您有一个任务正在下载")
            return
    
        is_downloading = True
        print("ddddd")
        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
    
        # head={"func":"download"}
        # head_data=json.dumps(head).encode("utf-8")
        # c.send(struct.pack("i",len(head_data)))
        # c.send(head_data)
    
    
    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"E:2019.11.3 GILclient111.py", "b.txt")
    
        is_uploading = False
    
        # head = {"func": "download"}
        # head_data = json.dumps(head).encode("utf-8")
        # c.send(struct.pack("i", len(head_data)))
        # c.send(head_data)
    
    
    func_dic = {"1": download, "2": upload}
    while True:
        res = input("请选择  1:上传  2:下载  Q:退出").strip()
        if res.upper() == "Q":
            break
        if res not in func_dic:
            print("error")
            continue
        Thread(target=func_dic[res]).start()
    
    服务器端:
    from threading import Thread
    import socket,json,struct
    from concurrent.futures import  ThreadPoolExecutor
    import common
    
    
    server=socket.socket()
    # server.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1)
    server.bind(("127.0.0.1",9090))
    server.listen(5)
    pool=ThreadPoolExecutor()
    
    
    
    
    def recv_file(c):
        common.recv_file(c)
    
        print("recv   ")
    
    def send_file(c):
        common.send_file(c)
    
        print("send   ")
    
    
    
    def task(c):
        """
        接收用户数据,判断用户要执行的操作
        :return:
        """
        # 先接收报头
        # c.recv(4),其为2进制需转
        while True:
    
            try:
                len_data=c.recv(4)
                if not len_data:
                    c.close()  # 需加while ,否则发送完毕就断开链接
                    break
                head_len=struct.unpack("i",len_data)[0]
    
                head=json.loads(c.recv(head_len).decode("utf-8"))
                if head["func"]=="download":
                    send_file(c)
                elif head["func"]=="upload":
                    recv_file(c)
                else:
                    print("require error")
            except ConnectionResetError:
                c.close()
                break
    2、基于多线程+TCP协议实现上传下载功能
    from threading import Thread
    import time
    msg_l=[]
    format_l=[]
    def receive():
        while True:
            msg=input(">>>:").strip()
            if not msg:
                continue
            msg_l.append(msg)
    
    
    def format_msg():
        while True:
            if msg_l:
                res=msg_l.pop(0)
                format_l.append(res.upper())
    
    def save():
        with open("db.txt","a",encoding="utf-8")as f:
            while True:
                if format_l:
                    res = format_l.pop(0)
                    f.write("%s
    ")%res
                time.sleep(0.1)
    
    
    if __name__ == '__main__':
        t1=Thread(target=receive)
        t2=Thread(target=format_msg)
        t3=Thread(target=save)
    
        t1.start()
        t2.start()
        t3.start()
    3、编写一个文本处理工具,总共三个任务: # 一个接收用户输入,一个将用户输入的内容格式化成大写,一个将格式化后的结果存入文件
  • 相关阅读:
    在peoplecode中直接调用SQR
    想起了李雷和韩梅梅
    结婚两周年纪念
    Unix Command Summary
    在PeopleSoft中如何隐藏菜单,导航栏,以及其他定制化链接
    那些朋友们
    整天工作的人为何当不了富翁
    ActiveX简单介绍
    SQL UNION
    Java程序设计问答大全(一)
  • 原文地址:https://www.cnblogs.com/dongzhihaoya/p/10210799.html
Copyright © 2020-2023  润新知