11.python线程

基本概念

1.进程

定义:  进程就是一个程序在一个数据集上的一次动态执行过程。

组成:  进程一般由程序、数据集、进程控制块三部分组成。

程序:  我们编写的程序用来描述进程要完成哪些功能以及如何完成;

数据集: 则是程序在执行过程中所需要使用的资源;

进程控制块: 用来记录进程的外部特征,描述进程的执行变化过程,系统可以利用它来控制和管理进程,它是系统感知进程存在的唯一标志

2.线程

    线程的出现是为了降低上下文切换的消耗,提高系统的并发性,并突破一个进程只能干一样事的缺陷,使到进程内并发成为可能。

  线程也叫轻量级进程,它是一个基本的CPU执行单元,也是程序执行过程中的最小单元

组成:由线程ID、程序计数器、寄存器集合和堆栈共同组成。

  线程的引入减小了程序并发执行时的开销,提高了操作系统的并发性能。线程没有自己的系统资源

3.线程与进程的区别

  1. 线程是执行的指令集 , 进程是资源的集合
  2. 线程的启动速度要比进程的启动速度要快
  3. 两个线程的执行速度是一样的
  4. 进程与线程的运行速度是没有可比性的
  5. 线程共享创建它的进程的内存空间 , 进程的内存是独立的
  6. 两个线程共享的数据都是同一份数据 , 两个子进程的数据不是共享的 , 而且数据是独立的
  7. 同一个进程的线程之间可以直接交流 , 同一个主进程的多个子进程之间是不可以进行交流 , 如果两个进程之间需要通信 , 就必须要通过一个中间代理来实现
  8. 一个新的线程很容易被创建 , 一个新的进程创建需要对父进程进行一次克隆
  9. 一个线程可以控制和操作同一个进程里的其他线程 , 线程与线程之间没有隶属关系 , 但是进程只能操作子进程
  10. 改变主线程 , 有可能会影响到其他线程的行为 , 但是对于父进程的修改是不会影响子进程

4.同步和异步

同步就是指一个进程在执行某个请求的时候,若该请求需要一段时间才能返回信息,那么这个进程将会一直等待下去,直到收到返回信息才继续执行下去;

异步是指进程不需要一直等下去,而是继续执行下面的操作,不管其他进程的状态。当有消息返回时系统会通知进程进行处理,这样可以提高执行的效率。

       举个例子,打电话时就是同步通信,发短息时就是异步通信

5.并行和并发

并行指系统具有处理多个任务(动作)的能力

并发是指系统具有同时处理多个任务(动作)的能力

 6.阻塞与非阻塞

阻塞调用是指调用结果返回之前,当前线程会被挂起(如遇到io操作)。函数只有在得到结果之后才会将阻塞的线程激活。

非阻塞和阻塞的概念相对应,指在不能立刻得到结果之前也会立刻返回,同时该函数不会阻塞当前线程

线程threading模块

import threading
import time

def run(n):         #定义线程要运行的函数
    print('task',n)
    time.sleep(2)

if __name__ == '__main__':
    t1 = threading.Thread(target=run,args=(1,))          #生成一个线程
    t2 = threading.Thread(target=run,args=(2,))
    t1.start()
    t2.start()

print('I am main thread')                #主线程

#这个进程里面有三个线程,1个主线程,t1,t2两个子线程
#子线程和主线程是同步开启的,主线程结束后,要等子线程全部结束后,进程才会关闭
import threading,time

class MyThread(threading.Thread):   #继承threading,Thread模块
    def __init__(self,n):
        super(MyThread, self).__init__()    #继承父类
        self.n = n

    def run(self):                 #必须用run
        print('task',self.n)
        time.sleep(2)

t1 = MyThread(1)
t2 = MyThread(2)
t1.start()
t2.start()

print('I am main thread')
print(t1.is_alive())       #返回线程是否活动的。
print(t1.getName())        #返回线程名。
t1.setName('我是T1')       #设置线程名。
print(t1.getName())
print(threading.currentThread())     #查看当前线程是主线程(mainThread)还是子线程(Thread)
print(threading.activeCount())       #返回正在运行的线程数量


结果:
task 1
task 2
I am main thread
True
Thread-1
我是T1
<_MainThread(MainThread, started 9580)>
3

Process finished with exit code 0

join和setDaemon

主线程 : 当一个程序启动时 , 就有一个进程被操作系统创建 , 与此同时一个线程也立刻运行 , 该线程通常叫做程序的主线程

子线程 : 因为程序是开始时就执行的 , 如果你需要再创建线程 , 那么创建的线程就是这个主线程的子线程

join的作用:是保证当前线程执行完成后,再执行其它线程

import threading
import time

def run(n):
    print("task ",n )
    time.sleep(2)

start_time = time.time()
t_objs = []    #存线程实例

for i in range(50):        #生成50个线程
    t = threading.Thread(target=run,args=("t-%s" %i ,))
    t.start()
    t_objs.append(t)  #为了不阻塞后面线程的启动,不在这里join,先放到一个列表里

for t in t_objs:      #循环线程实例列表,等待所有线程执行完毕
    t.join()

print("---all threads has finished...")
print("cost:",time.time() - start_time)

setDaemon

将线程声明为守护线程,必须在start() 方法调用之前设
setDaemon(),只要主线程完成了,不管子线程是否完成,都要和主线程一起退出
import threading
import time

def run(n):
    print('task',n)
    time.sleep(2)
    print('i am 子线程')     #主线程结束,setDaemon不管有没有运行完都会被销毁

if __name__ == '__main__':
    t1 = threading.Thread(target=run,args=(1,))
    t2 = threading.Thread(target=run,args=(2,))
    t1.setDaemon(True)   #设置守护线程,放在start之前
    t1.start()
    t2.setDaemon(True)
    t2.start()

print('I am main thread')


结果:
task 1
task 2
I am main thread

线程锁(互斥锁Mutex)

lock:如果有多个进程对同一文件进行修改 , 就会造成错乱 , 所以我们为了保护文件数据的安全 , 就需要给其进行加锁,join为整体串行 , lock为局部串行

Rlock:在线程间共享多个资源的时候,如果两个线程分别占有一部分资源并且同时等待对方的资源,就会造成死锁,因为系统判断这部分资源都正在使用,所有这两个线程在无外力作用下将一直等待下去。

           Rlock就是解决死锁的问题。

import time
import threading

def addNum():
    global num #在每个线程中都获取这个全局变量
    print('--get num:',num )
    time.sleep(1)
    lock.acquire() #修改数据前加锁
    num  -=1 #对此公共变量进行-1操作
    lock.release() #修改后释放

num = 100  #设定一个共享变量
thread_list = []
lock = threading.Lock() #生成全局锁
for i in range(100):
    t = threading.Thread(target=addNum)
    t.start()
    thread_list.append(t)

for t in thread_list: #等待所有线程执行完毕
    t.join()

print('final num:', num )
import threading, time


def run1():
    print("grab the first part data")
    lock.acquire()
    global num
    num += 1
    lock.release()
    return num


def run2():
    print("grab the second part data")
    lock.acquire()
    global num2
    num2 += 1
    lock.release()
    return num2


def run3():
    lock.acquire()
    res = run1()
    print('--------between run1 and run2-----')
    res2 = run2()
    lock.release()
    print(res, res2)


if __name__ == '__main__':

    num, num2 = 0, 0
    lock = threading.RLock()
    for i in range(10):
        t = threading.Thread(target=run3)
        t.start()

while threading.active_count() != 1:      1
    print(threading.active_count())
else:
    print('----all threads done---')
    print(num, num2)

semaphore(信号量)

import threading, time

def run(n):
    semaphore.acquire()
    time.sleep(1)
    print("run the thread: %s\n" % n)
    semaphore.release()

if __name__ == '__main__':
    semaphore = threading.BoundedSemaphore(5)  # 最多允许5个线程同时运行
    for i in range(22):
        t = threading.Thread(target=run, args=(i,))
        t.start()
while threading.active_count() != 1:
    pass
else:
    print('----all threads done---')

queue(队列)

import queue
#queue
#主要作用:解耦
 #         提高运行效率

q =queue.Queue()    #生成一个队列对象   先入先出
q.put('1')          #put item into the queue
q.put('2')
q.put('3')

q.qsize()          #看队列大小
q.get()            #从队列中取

q.get(block=True, timeout=None)  #取不到数据,默认阻塞,timeout设置阻塞时间
q.get_nowait()      #如果队列为空,取不到数据,抛出异常,不会阻塞卡主
q = queue.Queue(maxsize=3)         #maxsize可以设置队列的大小,最多允许存三个
q = queue.PriorityQueue           #优先级
print(q.full())       #判断队列是否有数据  返回blue值
print(q.empty())      #判断队列是否是空    返回blue值

生产者消费者模型

为什么要使用生产者和消费者模式

在线程世界里,生产者就是生产数据的线程,消费者就是消费数据的线程。在多线程开发当中,如果生产者处理速度很快,而消费者处理速度很慢,那么生产者就必须等待消费者处理完,才能继续生产数据。同样的道理,如果消费者的处理能力大于生产者,那么消费者就必须等待生产者。为了解决这个问题于是引入了生产者和消费者模式。

什么是生产者消费者模式

生产者消费者模式是通过一个容器来解决生产者和消费者的强耦合问题。生产者和消费者彼此之间不直接通讯,而通过阻塞队列来进行通讯,所以生产者生产完数据之后不用等待消费者处理,直接扔给阻塞队列,消费者不找生产者要数据,而是直接从阻塞队列里取,阻塞队列就相当于一个缓冲区,平衡了生产者和消费者的处理能力。

import threading,time
import queue

q = queue.Queue(maxsize=5)       #设置maxsize=5,防止生产过快

def Producer(name):    #生产者
    count = 1
    while True:
        q.put("面包%s" % count)
        print("%s生产了面包%s"%(name,count))
        count +=1
        time.sleep(0.1)

def  Consumer(name):         #消费者
    while True:
        print("[%s] 取到[%s] 并且吃了它..." %(name, q.get()))
        time.sleep(1)

#生成多个线程
p = threading.Thread(target=Producer,args=("derek",))
c = threading.Thread(target=Consumer,args=("chihuo1",))
c1 = threading.Thread(target=Consumer,args=("chihou2",))

p.start()
c.start()
c1.start()

Events

 event = threading.Event()

# a client thread can wait for the flag to be set event.wait()

# a server thread can set or reset it event.set() event.clear()

If the flag is set, the wait method doesn’t do anything. If the flag is cleared, wait will block until it becomes set again. Any number of threads may wait for the same event.

import time
import threading

event = threading.Event()

def lighter():     #红绿灯
    count = 0
    event.set()     #set flag 初始绿灯
    while True:
        if count >5 and count < 10: #改成红灯
            event.clear() #把标志位清了
            print("现在是红灯")
        elif count >10:
            event.set() #变绿灯
            count = 0
        else:
            print("现在是绿灯")
        time.sleep(1)
        count +=1

def car(name):
    while True:
        if event.is_set():   #判断the flag 是否is set 代表绿灯
            print("[%s] running..."% name )
            time.sleep(1)
        else:
            print("[%s] waiting ...... " %name)
            event.wait()   #等flag被set
            print("绿灯亮了,%s可以走了" %name)
            time.sleep(1)



light = threading.Thread(target=lighter,)
light.start()

car1 = threading.Thread(target=car,args=("Tesla",))
car1.start()

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏用户2442861的专栏

HTTP POST GET 本质区别详解

    一般在浏览器中输入网址访问资源都是通过GET方式;在FORM提交中,可以通过Method指定提交方式为GET或者POST,默认为GET提交

2521
来自专栏SDNLAB

ODL碳版本模块开发及流程梳理

文章主要基于ODL碳版本,进行简单插件的构建、安装、部署,以一个插件开发为例,介绍ODL新版本开发过程中的一些具体问题。 ? 一、碳版本简易开发流程 1.1 开...

5188
来自专栏Django Scrapy

Django安装及简单使用1.1

Django安装及简单使用 1.1 Django的简要概述 Django采用了MVC的软件设计模式,即模型M,视图V和控制器C。 代码都在github: U...

31311
来自专栏LanceToBigData

linux(七)之linux系统中查找文件

前面介绍一篇文章介绍了关于vi编辑器的使用,感觉是不是那么多的命令怎么记得住呀,小编也是这样让认为的,但是慢慢的发现,其实还是很有意思的。正所谓熟能生巧多练习,...

27710
来自专栏PHP在线

PHP使用SOAP调用.net的WebService问题

项目的需求,需要和一个.net系统进行数据交换,合作方提供了一个WebService接口。这个与一般的PHP POST或GET传值再查库拿数据的思路有点不一样,...

3476
来自专栏逆向技术

脱壳第一讲,手工脱壳ASPack2.12的壳.ESP定律

      脱壳第一讲,手工脱壳ASPack2.12的壳.ESP定律 一丶什么是ESP定律 首先我们要明白什么是壳.壳的作用就是加密PE的. 而ESP定律就是壳...

2626
来自专栏程序员宝库

Linux 基础:文件查找 find

在linux的日常管理中,find的使用频率很高,熟练掌握对提高工作效率很有帮助。 find的语法比较简单,常用参数的就那么几个,比如 -name、 -type...

3026
来自专栏Java技术栈

单点登录终极方案之 CAS 应用及原理

Cookie的单点登录的实现方式很简单,但是也问题颇多。例如:用户名密码不停传送,增加了被盗号的可能。另外,不能跨域!

2382
来自专栏Java Web

初学Java Web(5)——cookie-session学习

HTTP 协议 Web 浏览器与 Web 服务器之间的一问一答的交互过程必须遵守一定的规则,这样的规则就是 HTTP 协议。 HTTP 是 hypertext ...

3085
来自专栏区块链

如何基于Python写一个TCP反向连接后门

0x0 介绍 在Linux系统做未授权测试,我们须准备一个安全的渗透环境,通常第一件事就是安装虚拟机。且在攻击阶段中还需要对受害系统进行控制。常见的后门大多数是...

3157

扫码关注云+社区