网络编程之线程

  1. 什么是线程?

    程序的执行线路。每个进程默认有一条线程。线程包含了程序的具体步骤。

    多线程就是一个进程中有除主线程(默认线程)外还有多个线程。

  2. 线程与进程的关系(进程包含线程,而线程依赖进程存在)

    1.进程包含了运行该程序的所有资源,是一个资源单位。

    2.线程是CPU的执行单位(最小的执行单位)。

    3.进程一旦被创建,就默认开启了一条线程,称之为主线程。

    4.线程共享创建它的进程的地址空间;进程有自己的地址空间。

    5.线程可以直接访问其进程的数据段;进程有它们自己的父进程的数据段副本。

    6.线程可以直接与进程的其他线程通信;进程必须使用进程间通信来与兄弟进程通信。

    7.容易创建新线程;新进程需要父进程的复制。

    8.线程可以对同一进程的线程进行相当大的控制;流程只能对子流程进行控制。

    9.对主线程的更改(取消、优先级更改等)可能会影响进程中其他线程的行为;对父进程的更改不会影响子进程。

  3. 为什么使用多线程?

    为了提高程序运行效率。与进程区别是线程对于系统资源的占用非常小。

    1.多个线程共享一个进程的地址空间。

    2.线程比进程对系统资源占用小,创建速度快10-100倍。

    3.同一个进程中多个线程之间资源共享,不需要像进程一样需要进程间通信。

  4. 线程的两种方式

    #第一种方式:导入threading模块中的Thread类来创建一个对象
    from threading import Thread

    def task():
    print('子线程 running。。。')
    t = Thread(target=task)
    t.start()
    print('over')
    # from threading import Thread
    #
    # def task():
    # print('子线程 running。。。')
    #
    # if __name__ == '__main__':
    # t = Thread(target=task)
    # t.start()
    # print('over')


    #第二种方式:创建一个子类继承Thread类,可自定义run方法,但是不能改变run方法名。
    from threading import Thread
    class MyThread(Thread):
    def __init__(self, name):
    super().__init__()
    self.name = name
    def run(self):
    print('%s say hi' % self.name)
    if __name__ == '__main__':
    t = MyThread('daidai')
    t.start()
  5. 进程和线程的对比

    from multiprocessing import Process
    from threading import Thread
    import time

    def task():
    pass
    #开启100个线程花费的时间
    start_time = time.time()
    ts = []
    for i in range(100):
    t = Thread(target=task)
    t.start()
    ts.append(t)
    print(os.getpid()) # 所有的进程编号都一样,同属一个进程中
    for t in ts:
    t.join()
    print(time.time()-start_time)
    ###
    0.018949270248413086

    #开启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)
    ###
    5.281934499740601
  6. 线程间的资源共享

    from threading import Thread
    x = 100
    def task():
    print("run....")
    global x # 修改全局变量
    x = 0
    t = Thread(target=task)
    t.start()
    t.join() # join方法将子线程的优先级提高到主线程前
    print(x)
    print("over")
    ####
    run....
    0
    over
  7. 守护线程

    无论是进程还是线程:都遵循守护一方等待主方运行完毕后被销毁。运行完毕不是终止运行。

    1.对于主进程来说,运行完毕是主代码运行完毕。

    2.对于主线程来说,运行完毕是主线程内所有非守护线程运行完毕。所以守护线程会在所有非守护线程结束后结束。

    详细解释:

    #1.主进程在其代码结束后就已经算运行完毕(守护进程在此时会被回收),然后著京城会一直等着非守护的子进程都运行完毕后回收子进程的资源(否则就会产生僵尸进程),才会结束。

    #2.主线程在其他非守护线程运行完毕后才算运行完毕(守护线程在此时就会被回收)。因为主线程的结束意味着进程的结束,进程整体的资源都将被回收,而进程必须保证非守护线程都运行完毕后才能结束。

    from threading import Thread
    import time

    def task():
    print('sub thread running')
    time.sleep(3)
    print('sub thread over')

    t = Thread(target=task)
    t.setDaemon(True) # 要在开启之前设置
    t.start()
    print('main thread over')
    ###
    sub thread running
    main thread over
     
  8. Thread类对象常用的属性和方法

    对象的方法:

    isAlive():返回线程是否活动

    getName():返回线程名

    setName():设置线程名

    threading模块的一些方法:

    current_thread():返回当前线程变量,获取当前线程

    enumerate():返回一个包含正在运行线程的列表

    active_count():返回正在进行的线程数量,与len(threading.enumerate())有相同的结果。

    from threading import Thread,current_thread,enumerate,active_count
    import time
    import os
    def task():
    time.sleep(3)
    print(current_thread().getName())
    t = Thread(target = task)
    t.start()
    print(current_thread().getName())
    print(current_thread())
    print(enumerate())
    print(active_count())
    print('daidai')
    #运行结果
    MainThread
    <_MainThread(MainThread, started 14652)>
    [<_MainThread(MainThread, started 14652)>, <Thread(Thread-1, started 15548)>]
    2
    daidai
    Thread-1


    t.join() #主线程等待子线程结束

    线程互斥锁
    当多个进程或者多个线程需要同时修改同一份数据时,可能造成数据的错乱,所以需要给说句加上锁。
    同样的线程中也有死锁和可重入锁RLock
    import time
    from threading import Thread, Lock
    lock = Lock()
    a = 100
    def task():
    lock.acquire()
    global a # 修改全局变量a
    temp = a-1
    time.sleep(0.05)
    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) # 全局中的a已经被修改为0
  9. 信号量Semaphore

    semaphore管理一个内置的计数器,每当调用acquire()时内置计数器-1:

    调用release()时内置计数器+1;

    计数器不能小于0;当计数器为0时,acquire()将阻塞线程直到其他线程调用release()。

    信号量其实也是一种锁,特点是可以设置一个数据可以被几个线程(进程)共享。

    与普通锁的区别:

    普通锁一旦加锁就意味着这个数据在同一时间只能被一个线程或进程使用。

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

    #实例:开启10个线程,每次运行3个
    from threading import Semaphore, Thread, current_thread
    import time

    sem = Semaphore(3)
    def task():
    sem.acquire()
    print('%s task running' % current_thread())
    time.sleep(3)
    sem.release()

    for i in range(10):
    t = Thread(target=task)
    t.start()
  10. 生产者消费者模型中的JoinableQueue

    JoinableQueue类是一种队列,Queue的子类。但是实例化的对象可以明确直到队列中是否有数据及数据的使用量。

    参数介绍:

    maxsize是队列中允许最大项数,省略则无大小限制。

    方法介绍:

    JoinableQueue的实例除了与Queue实例对象相同方法之外还有:

    task_done():使用者使用此方法发出信号,表示q.get()的返回项目已经被处理。如果调用此方法的次数大于从队列中删除项目的数量,将引发异常。

    q.join():明确生产者不会再生产数据。加入数据到队列中。

    import time
    import random
    from multiprocessing import Process, JoinableQueue
    def eat_hotdog(name, q):
    while True:
    res = q.get()
    if not res:
    print('吃完了。。。')
    break
    print('%s吃了%s' % (name, res))
    time.sleep(random.randint(1, 2))
    q.task_done() #向q.join()发送一次信号,证明一个数据已经被取走了
    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() # 明确生产方已经不会再生成数据了

python中网络编程之线程的更多相关文章

  1. python中网络编程基础

    一:什么是c\s架构 1.c\s即client\server 客户端\服务端架构. 客户端因特定的请求而联系服务器并发送必要的数据等待服务器的回应最后完成请求 服务端:存在的意义就是等待客户端的请求, ...

  2. python中网络编程

    网络编程软件架构介绍: C/S:客户端,服务端 B/S:浏览器,服务端 # 常见应用: 1.手机端看着感觉是c/s架构其实更多的是b/s架构,例如微信小程序,支付宝第三方接口 2.pc端:b/s比较火 ...

  3. Python中网络编程对 listen 函数的理解

    listen函数的第一个参数时SOCKET类型的,该函数的作用是在这个SOCKET句柄上建立监听,至于有没有客户端连接进来,就需要accept函数去进行检查了,accept函数的第一个参数也是SOCK ...

  4. Python中网络编程对socket accept函数的理解

    在服务器端,socket()返回的套接字用于监听(listen)和接受(accept),这个套接字不能用于与客户端之间发送和接收数据. accept()接受一个客户端的连接请求,并返回一个新的套接字, ...

  5. Python高级网络编程系列之第一篇

    在上一篇中我们简单的说了一下Python中网络编程的基础知识(相关API就不解释了),其中还有什么细节的知识点没有进行说明,如什么是TCP/IP协议有几种状态,什么是TCP三次握手,什么是TCP四次握 ...

  6. python网络编程基础(线程与进程、并行与并发、同步与异步、阻塞与非阻塞、CPU密集型与IO密集型)

    python网络编程基础(线程与进程.并行与并发.同步与异步.阻塞与非阻塞.CPU密集型与IO密集型) 目录 线程与进程 并行与并发 同步与异步 阻塞与非阻塞 CPU密集型与IO密集型 线程与进程 进 ...

  7. python之网络编程

    本地的进程间通信(IPC)有很多种方式,但可以总结为下面4类: 消息传递(管道.FIFO.消息队列) 同步(互斥量.条件变量.读写锁.文件和写记录锁.信号量) 共享内存(匿名的和具名的) 远程过程调用 ...

  8. python中的进程、线程(threading、multiprocessing、Queue、subprocess)

    Python中的进程与线程 学习知识,我们不但要知其然,还是知其所以然.你做到了你就比别人NB. 我们先了解一下什么是进程和线程. 进程与线程的历史 我们都知道计算机是由硬件和软件组成的.硬件中的CP ...

  9. python基础网络编程--转

    python之网络编程 本地的进程间通信(IPC)有很多种方式,但可以总结为下面4类: 消息传递(管道.FIFO.消息队列) 同步(互斥量.条件变量.读写锁.文件和写记录锁.信号量) 共享内存(匿名的 ...

随机推荐

  1. C语言题库----指针

    1.如果f是一个函数,请说明 f() 和f的意思. f是函数的地址,f()是函数 2.怎样理解数组的下标运算? 先偏移,后取址. 3.int *p,*q; int a[]={10,20,30,40}; ...

  2. 当yum安装出现Error: Package: glibc-headers .....时

    环境 CentOS Linux release 7.4.1708 (Core)   当使用yum源安装时,出现以下报错 Error: Package: glibc-headers-.el7_4..x8 ...

  3. python selenium Chrome模拟手机浏览器(十七)

    在做移动端页面测试时可以利用Chrome mobile emulation 辅助完成页面的适配问题,但是目前手机市场上的型号居多我们也没有办法通过人工的模式一一的去适配,所以这里考虑到通过自动化的模式 ...

  4. redis订阅发布消息操作本地缓存

    Redis 本地缓存+远程缓存方案 使用纯java的ehcache作为本地缓存 Reids 作为远程分布式缓存 解决redis缓存压力过大,提高缓存速度,以及缓存性能. Redis和ehcache缓存 ...

  5. Django-Form组件之字段

    Form类 创建Form类时,主要涉及到 [字段] 和 [插件],字段用于对用户请求数据的验证,插件用于自动生成HTML; 1.Django内置字段如下: 1 2 3 4 5 6 7 8 9 10 1 ...

  6. Linux下mysql的root密码修改方法(ERROR 1054)

    #1.停止mysql数据库 /etc/init.d/mysqld stop #2.执行如下命令 mysqld_safe --user=mysql --skip-grant-tables --skip- ...

  7. Java中所涉及到的设计模式小记

    一.JAVA设计模式一共有23中.其中这23中大体可以分为3类,具体分法如下所示: 1.创建型模式:涉及到的设计模式共5种,分别是: 工厂方法模式.抽象工厂模式.单例模式.建造者模式.原型模式 2.结 ...

  8. Oracle 外键级联更新

    Oracle数据库中,外键约束只允许级联删除,不允许级联更新,因此,如果想要实现主表数据更新后,子表外键自动更新,只能取消外键关系,通过前端程序来维护实现完整引用,一个代替的解决方案是使用延迟约束和触 ...

  9. RDLC报表系列--------初级报表

    前面记录下了很多平时开发遇到的问题,RLDC之前也是不会,只会水晶报表,后来也慢慢的也上手了.把这些记录下来,以后用的着 1.打开VS添加新建项,选择Reporting,选择报表,后缀名为RLDC的名 ...

  10. locate中使用variant

    利用locate构造多字段查询,采用variant很方便,简介如下, //构造查询多字段,例如'编号;姓名'形式 aLookField :=  FieldByName ('关键字1').AsStrin ...