首页 > 其他分享 >线程

线程

时间:2023-04-01 17:13:13浏览次数:36  
标签:__ name Thread 进程 线程 print

目录

线程

线程概念的引入背景

进程

  之前我们已经了解了操作系统中进程的概念,程序并不能单独运行,只有将程序装载到内存中,系统为它分配资源才能运行,而这种执行的程序就称之为进程。程序和进程的区别就在于:程序是指令的集合,它是进程运行的静态描述文本;进程是程序的一次执行活动,属于动态概念。在多道编程中,我们允许多个程序同时加载到内存中,在操作系统的调度下,可以实现并发地执行。这是这样的设计,大大提高了CPU的利用率。进程的出现让每个用户感觉到自己独享CPU,因此,进程就是为了在CPU上实现多道编程而提出的。

有了进程为什么要有线程

  进程有很多优点,它提供了多道编程,让我们感觉我们每个人都拥有自己的CPU和其他资源,可以提高计算机的利用率。很多人就不理解了,既然进程这么优秀,为什么还要线程呢?其实,仔细观察就会发现进程还是有很多缺陷的,主要体现在两点上:

  • 进程只能在一个时间干一件事,如果想同时干两件事或多件事,进程就无能为力了。

  • 进程在执行的过程中如果阻塞,例如等待输入,整个进程就会挂起,即使进程中有些工作不依赖于输入的数据,也将无法执行。

  现在你应该明白了进程的缺陷了,而解决的办法很简单,我们完全可以让独立的过程,并行起来。而实际的操作系统中,也同样引入了这种类似的机制——线程。

进程和线程

  • 进程:进程其实是资源单位,表示一块内存空间
  • 线程:线程才是执行单位,表示真正的代码指令
1.一个进程内可以开设多个线程
2.同一个进程下的多个线程数据是共享的
3.创建进程与线程的区别
	创建进程的消耗要远远大于线程
4.同一个进程下多线程数据是共享的,多进程下的不同线程要通信如何做----->等同于进程间通信

当你在右键运行文件时,进程就已经创建了,这些代码的执行就是主线程。进程仅仅是一个内存空间而已,这块空间做了什么事情,为这些代码的运行提供必备条件,名称空间、内置空间、python解释器等等。

进程是资源分配的基本单位,线程是CPU执行的最小单位,每一个进程中至少有一个线程
当进程中只有一个线程的时候,这个线程叫主线程
# 操作系统来调度进程和线程,程序员级别是不能调度他们的

协程:就是有我们程序员级别来调度的(用户态)
进程   >>> 线程  >>> 协程
# 进程占用的资源是最多的,其次是线程占用的资源,最后是协程.
'''在python中开多进程,多协程,不开多线程,(GIL锁)解释器锁会讲到
在其他语言中,都是选择开多线程
'''

创建线程

创建线程的两种方式

from threading import Thread

def write():
    with open('a.txt', 'w', encoding='utf-8') as f:
        f.write('hello')

# 在Windows中,开线程不需要写在if __name__ == '__main__':中,但是我们尽量都写一下
if __name__ == '__main__':  
    # p = Process(target=write, )
    # p.start()
    # 开线程
    t = Thread(target=write, )
    t.start()  # 异步操作
    print(123)
    
解析:
py文件是一个进程,里面开了两个线程,主线程是打印东西(没有打印也肯定会有一个主线程的,因为一个进程至少有一个线程),然后导入模块又开启了一个子线程
"""
创建线程无需考虑反复执行的问题
"""

创建线程的第二种方式

class MyThread(Thread):
    def run(self):
        print('run is running')
        time.sleep(1)
        print('run is over')

obj = MyThread()
obj.start()
print('主线程')
# > 输出结果是:
run is running
主线程
run is over

参数和方法

参数和方法:
1.name=None
2.args=()
3.kwargs=None
4.t.join():先运行子线程,再运行主线程
5.守护线程:主线程结束,子线程结束
	t.daemon = True
    t.setDaemon(True)  # 两行代码都可以

Thread类中的几个方法

from threading import Thread

def write():
    with open('a.txt', 'w', encoding='utf-8') as f:
        f.write('hello')
    
    
def task():
    print('子线程:', t.name)

if __name__ == '__main__':
	'''线程的几个方法'''
    t = Thread(target=task, )
    t.start()

    print(t.is_alive())  # 判断线程是否存活
    print(t.name)  # 返回线程名
    print(t.getName())  # 返回线程名
    t.setName('ly is handsome')  # 设置线程名
    print(t.getName())  # ly is handsome

threading模块的几个方法

from threading import Thread
import threading
def task():
    # print('子线程:', t.name)  # 子线程: Thread-1
    print(threading.currentThread())  # <Thread(Thread-1, started 5376)>
    print(threading.currentThread().getName())  # Thread-1

if __name__ == '__main__':
    t = Thread(target=task, )
    t.start()
    '''threading模块的几个方法'''
    # print(t)  # <Thread(Thread-1, started 5376)>
    # 当前线程
    print(threading.currentThread())  # <_MainThread(MainThread, started 12840)>
    # 当前线程的名字
    print(threading.currentThread().getName())  # MainThread

如何开启多线程

from threading import Thread

def task():
    print('子线程:', t.name)

if __name__ == '__main__':
    ll = []
    for i in range(10):
        t = Thread(target=task, )
        t.start()
        ll.append(t)  # 串行
    for t in ll:  # for循环列表线程对象,内部join()
        t.join()
    print('end...')

进程和线程的比较

  1. 进程的开销远远大于线程的开销
    进程里面可以开很多个线程

  2. 进程能利用多核优势的,线程就不能利用多核优势
    但是,可以开启多个进程,在进程里面在开启线程

  3. 进程之间的数据隔离的,线程之间的数据是不隔离的,前提:同一进程之间
    不同进程下的线程之间数据是不共享的---->如何让不同进程下的线程之间数据共享--->其实就是进程间通信

1.pid不同

from threading import Thread
from multiprocessing import Process
import os

def work():
    print('hello',os.getpid())

if __name__ == '__main__':
    # part1:在主进程下开启多个线程,每个线程都跟主进程的pid一样
    t1=Thread(target=work)
    t2=Thread(target=work)
    t1.start()
    t2.start()
    print('主线程/主进程pid',os.getpid())
    # 开进程消耗的资源比较大
	'''
    输出结果:
    hello 20080
	hello 20080
	主线程pid 20080
    '''
    
    # part2:开多个进程,每个进程都有不同的pid
    p1=Process(target=work)
    p2=Process(target=work)
    p1.start()
    p2.start()
    print('主进程pid',os.getpid())
    '''
    输出结果:
    主进程pid 31124
    hello 25536
    hello 39732
    '''

2.开启效率的不同

from threading import Thread
from multiprocessing import Process
import os

def work():
    import time
    time.sleep(1)
    print('hello')

if __name__ == '__main__':
    import time
    ctime = time.time()
    # 在主进程下开启线程
    t = Thread(target=work)
    t.start()
    t.join()
    print('主线程')
    print('time:', time.time() - ctime)
    '''
    输出结果:
    hello
	主线程
	time: 1.0179188251495361
    '''

    # 在主进程下开启子进程
    p = Process(target=work)
    p.start()
    p.join()
    print('主进程')
    print('time:', time.time() - ctime)
    '''
    hello
	主进程
	time: 1.1995644569396973
    '''
  
  
不加join()时,
开进程消耗的资源比较多,会先运行出主进程
开线程消耗的资源比较小,所以会先运行子线程

3.内存数据的共享不同

from  threading import Thread
from multiprocessing import Process
import os
def work():
    global n
    n=0

if __name__ == '__main__':
    n = 100
    p = Process(target=work)
    p.start()
    p.join()
    print('主',n)  # 毫无疑问子进程p已经将自己的全局的n改成了0,但改的仅仅是它自己的,查看父进程的n仍然为100


    n = 1
    t = Thread(target=work)
    t.start()
    t.join()
    print('主',n) # 查看结果为0,因为同一进程内的线程之间共享进程内的数据

多线程实现socket

服务端

import multiprocessing
import threading

import socket
s=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
s.bind(('127.0.0.1',8080))
s.listen(5)

def action(conn):
    while True:
        data=conn.recv(1024)
        print(data)
        conn.send(data.upper())

if __name__ == '__main__':

    while True:
        conn,addr=s.accept()
        p=threading.Thread(target=action,args=(conn,))
        p.start()

客户端

import socket

s=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
s.connect(('127.0.0.1',8080))

while True:
    msg=input('>>: ').strip()
    if not msg:continue

    s.send(msg.encode('utf-8'))
    data=s.recv(1024)
    print(data)

守护线程

# 当主线程运行结束,子线程会跟着结束
import threading

def run(n):
    for i in range(n):
        print(threading.current_thread().name + "  " + str(i))
if __name__ == "__main__":
    t = threading.Thread(target=run, args=(100,), name='后台线程')    
    t.daemon = True # 将此线程设置成后台线程
    t.start() # 启动后台线程
    # t.join()
    for i in range(10):
        print(threading.current_thread().name + "  " + str(i))


解析:
主函数中的for循环和线程t是两个不同的线程,其中for循环是主线程,当把t设置为守护线程时,主线程for循环运行结束后t线程会同时结束。

GIL锁(全局解释器锁)

  Python代码的执行由Python虚拟机(也叫解释器主循环)来控制。Python在设计之初就考虑到要在主循环中,同时只有一个线程在执行。虽然 Python 解释器中可以“运行”多个线程,但在任意时刻只有一个线程在解释器中运行。
  对Python虚拟机的访问由全局解释器锁(GIL)来控制,正是这个锁能保证同一时刻只有一个线程在运行。

1. python代码运行在解释器之上,有解释器来翻译执行
2. python解释器的种类有哪些?
	CPython  IPython PyPy Jython
3. GIL锁存在于CPython解释器中
4. 市面上目前绝大多数(95%)都使用的是CPython解释器
5. 起一个垃圾回收线程,在起一个正常执行代码的线程,当垃圾回收线程还没回收完毕,其他线程有可能会抢夺资源,这种情况在python设计之处就不允许的
6. python在设计之处,就在python解释器之上加了一把锁(GIL锁),加这个锁的目的是:同一时刻只能有一个线程执行,不能同时有多个线程执行,如果开了多个线程,那么,线程要想有执行权限,必须先拿到这把锁(GIL锁)
"""只需要记住:同一时刻多个线程只能有一个线程在运行,其他线程都处于等待状态"""

理解记忆部分

1. python有GIL锁的原因,同一个进程下多个线程实际上同一时刻,只有一个线程在执行
2. 只有在python上开进程用的多,其他语言一般不开多进程,只开多线程就够了
3. cpython解释器开多线程不能利用多核优势,只有开多进程才能利用多核优势,其他语言不存在这个问题
4. 8核cpu电脑,充分利用起我这个8核,至少起8个线程,8条线程全是计算--->计算机cpu使用率是100%,
5. 如果不存在GIL锁,一个进程下,开启8个线程,它就能够充分利用cpu资源,跑满cpu
6. cpython解释器中好多代码,模块都是基于GIL锁机制写起来的,改不了了--->我们不能有8个核,但我现在只能用1核,---->开启多进程--->每个进程下开启的线程,可以被多个cpu调度执行
7. cpython解释器:
  io密集型使用多线程
      不需要计算,一般不消耗CPU,所以就选择线程比较好
  计算密集型使用多进程
      在消耗CPU,所以做好选择进程


  # -io密集型,遇到io操作会切换cpu,假设你开了8个线程,8个线程都有io操作--->io操作不消耗cpu--->一段时间内看上去,其实8个线程都执行了
  
  # -计算密集型,消耗cpu,如果开了8个线程,第一个线程会一直占着cpu,而不会调度到其他线程执行,其他7个线程根本没执行,所以我们开8个进程,每个进程有一个线程,8个进程下的线程会被8个cpu执行,从而效率高
    
'''计算密集型选多进程好一些,在其他语言中,都是选择多线程,而不选择多进程.'''  

官方文档对GIL的解释
In CPython, the global interpreter lock, or GIL, is a mutex that prevents multiple native threads from executing Python bytecodes at once. This lock is necessary mainly because CPython’s memory management is not thread-safe. (However, since the GIL exists, other features have grown to depend on the guarantees that it enforces.

验证GIL的存在

from threading import Thread

num = 100

def task():
    global num
    num -= 1


t_list = []
for i in range(100):
    t = Thread(target=task)
    t.start()
    t_list.append(t)  # 想要每个线程都加上join()
for t in t_list:
    t.join()
print(num)  # 0

解析:有GIL存在,同一时间只会有一个线程在运行


中间有睡眠1秒,第一次线程1抢到了GIL锁,开始运行自己的代码,count = 100, 运行到time.sleep(),有io操作,CPU会立刻走,CPU走GIL锁会自动释放。锁就给剩下的线程去抢。与上步骤相同,所有的线程中的count都等于100。睡眠结束,又公平的去抢锁,一个线程抢到后,运行剩下的代码减去1,所以所有的线程的结果都是99。

GIL与普通互斥锁

既然CPython解释器中有GIL 那么我们以后写代码是不是就不需要操作锁了!!!'''面试题'''
"""
GIL的作用是有限的,不同的数据要加不同的锁
GIL只能够确保同进程内多线程数据不会被垃圾回收机制弄乱 
	并不能确保程序里面的数据是否安全
"""
# 要想保证代码层面的数据安全,还是要自己加锁
import time
from threading import Thread,Lock

num = 100


def task(mutex):
    global num
    mutex.acquire()
    count = num
    time.sleep(0.1)
    num = count - 1
    mutex.release()


mutex = Lock()
t_list = []
for i in range(100):
    t = Thread(target=task,args=(mutex,))
    t.start()
    t_list.append(t)
for t in t_list:
    t.join()
print(num)  # 0把并发变成了串行

多线程验证I/O密集型和计算密集型

1.I/O密集型

def work():
    time.sleep(2)   # 模拟纯IO操作


if __name__ == '__main__':
    start_time = time.time()
    # t_list = []
    # for i in range(100):
    #     t = Thread(target=work)
    #     t.start()
    #     t_list.qppend(t)
    # for t in t_list:
    #     t.join()
    p_list = []  # 100个进程
    for i in range(100):
        p = Process(target=work)
        p.start()
        t_list.qppend(t)
    for p in p_list:
        p.join()
    print('总耗时:%s' % (time.time() - start_time))

"""
IO密集型
    多线程:2.0149583816528320
    多进程:3.6402878761291504
"""

2.计算密集型

from threading import Thread
from multiprocessing import Process
import os
import time


def work():
    # 计算密集型
    res = 1
    for i in range(1, 100000):
        res *= i


if __name__ == '__main__':
    # print(os.cpu_count())  # 12  查看当前计算机CPU个数
    start_time = time.time()
    # p_list = []
    # for i in range(12):  # 一次性创建12个进程
    #     p = Process(target=work)
    #     p.start()
    #     p_list.append(p)
    # for p in p_list:  # 确保所有的进程全部运行完毕
    #     p.join()
    t_list = []
    for i in range(12):
        t = Thread(target=work)
        t.start()
        t_list.append(t)
    for t in t_list:
        t.join()
    print('总耗时:%s' % (time.time() - start_time))  # 获取总的耗时

"""
计算密集型
    多进程:5.665567398071289
    多线程:30.233906745910645
"""

同步锁(互斥锁)

存在一个问题:开多进程或者多线程一定好吗?
不一定,虽然可以提高效率,但是,会出现安全问题面试题

多线程同时操作一个数据,会产生数据错乱,并引发安全问题--->加锁--->让原本并发的操作,变成串行,牺牲效率,保证安全--->通过线程queue也可以避免并发安全的问题,所有queue的本质就是锁

代码进行操作:
1.开10个线程改全局n

from threading import Thread
n = 10

def task():
    global n
    n -= 1

if __name__ == '__main__':
    for i in range(10):
        t = Thread(target=task)
        t.start()
    print('n:', n)

# > 输出结果是:n: 0
解析:开的是线程,数据之间是互通的,可以更改

2.把n -= 1变形成两步

from threading import Thread

n = 10

import time
def task():
    global n
    # n -= 1  # 变形
    temp = n
    n = temp - 1

if __name__ == '__main__':
    ll = []
    for i in range(10):
        t = Thread(target=task)
        t.start()
        ll.append(t)
    for j in ll:
        j.join()
    print('n:', n)

# > 输出结果是:n: 0

3.子线程中间加了阻塞

from threading import Thread

n = 10

import time
def task():
    global n
    # n -= 1  # 变形
    temp = n
    time.sleep(1)
    n = temp - 1

if __name__ == '__main__':
    ll = []
    for i in range(10):
        t = Thread(target=task)
        t.start()
        ll.append(t)
    for j in ll:
        j.join()
    print('n:', n)

# > 输出结果是:n: 9

解析:
10个线程一个一个运行,第一个Thread-1运行执行了前两步后遇到了阻塞(睡眠1秒),CPU切换执行第二个线程,也是执行了前两步后遇到了阻塞,CPU切换执行下一个,以此类推,10个线程都阻塞在睡眠一秒的位置,此时所有的线程中的n都是10,睡眠结束后,10个线程都是执行10-1=9这一步,所以最后n的结果是9。

互斥锁的使用

4.加上互斥锁

from threading import Thread, Lock
n = 10
def task(lock):
    global n
    lock.acquire()  # 只要有一个线程进来了,其他线程都要等着
    # n -= 1  # 变形
    temp = n
    time.sleep(0.01)
    n = temp - 1
    lock.release()


if __name__ == '__main__':
    ll = []
    lock = Lock()
    for i in range(10):
        t = Thread(target=task, args=(lock,))
        t.start()
        ll.append(t)
    for j in ll:
        j.join()
    print('n:', n)  # 结果肯定为0,由原来的并发执行变成串行,牺牲了执行效率保证了数据安全

# > 输出结果是:n: 0

死锁与递归锁

死锁现象(了解)

死锁:是指两个或两个以上的进程或线程在执行过程中,因争夺资源而造成的一种互相等待的现象。所以单进程和单线程不会出现死锁现象

acquire()
release()

我们很少会编写抢锁和释放锁的代码,因为很容易会出现问题

死锁现象代码:

from threading import Thread,Lock
import time

mutexA = Lock()  # 产生一把锁
mutexB = Lock()  # 产生一把锁


class MyThread(Thread):
    def run(self):
        self.func1()
        self.func2()

    def func1(self):
        mutexA.acquire()
        print(f'{self.name}抢到了A锁')
        mutexB.acquire()
        print(f'{self.name}抢到了B锁')
        mutexB.release()
        print(f'{self.name}释放了B锁')
        mutexA.release()
        print(f'{self.name}释放了A锁')

    def func2(self):
        mutexB.acquire()
        print(f'{self.name}抢到了B锁')
        time.sleep(1)  # 加一个睡眠
        mutexA.acquire()
        print(f'{self.name}抢到了A锁')
        mutexA.release()
        print(f'{self.name}释放了A锁')
        mutexB.release()
        print(f'{self.name}释放了B锁')

for i in range(10):
    obj = MyThread()
    obj.start()
    
'''
Thread-1抢到了A锁
Thread-1抢到了B锁
Thread-1释放了B锁
Thread-1释放了A锁
Thread-1抢到了B锁
Thread-2抢到了A锁
然后就卡住,死锁了
'''

递归锁

解决方法,递归锁,在Python中为了支持在同一线程中多次请求同一资源,python提供了可重入锁RLock。

这个RLock内部维护着一个Lock和一个counter变量,counter记录了acquire的次数,从而使得资源可以被多次require。直到一个线程所有的acquire都被release,其他的线程才能获得资源。上面的例子如果使用RLock代替Lock,则不会发生死锁:

from threading import Thread,RLock
import time

mutexA = mutexB = RLock()  # 使用递归锁,也叫可重入锁


class MyThread(Thread):
    def run(self):
        self.func1()
        self.func2()

    def func1(self):
        mutexA.acquire()
        print(f'{self.name}抢到了A锁')
        mutexB.acquire()
        print(f'{self.name}抢到了B锁')
        mutexB.release()
        print(f'{self.name}释放了B锁')
        mutexA.release()
        print(f'{self.name}释放了A锁')

    def func2(self):
        mutexB.acquire()
        print(f'{self.name}抢到了B锁')
        time.sleep(1)  # 加一个睡眠
        mutexA.acquire()
        print(f'{self.name}抢到了A锁')
        mutexA.release()
        print(f'{self.name}释放了A锁')
        mutexB.release()
        print(f'{self.name}释放了B锁')

for i in range(10):
    obj = MyThread()
    obj.start()

典型问题:哲学家吃面问题

import time
from threading import Thread, Lock, RLock

noodle_lock = Lock()
fork_lock = Lock()

def eat1(name):
    noodle_lock.acquire()
    print('%s 抢到了面条' % name)
    fork_lock.acquire()
    print('%s 抢到了叉子' % name)
    print('%s 吃面' % name)
    fork_lock.release()
    noodle_lock.release()


def eat2(name):
    fork_lock.acquire()
    print('%s 抢到了叉子' % name)
    time.sleep(1)
    noodle_lock.acquire()
    print('%s 抢到了面条' % name)
    print('%s 吃面' % name)
    noodle_lock.release()
    fork_lock.release()


for name in ['哪吒', 'jack', 'tank']:
    t1 = Thread(target=eat1, args=(name,))
    t2 = Thread(target=eat2, args=(name,))
    t1.start()
    t2.start()
'''
哪吒 抢到了面条
哪吒 抢到了叉子
哪吒 吃面
哪吒 抢到了叉子
jack 抢到了面条
卡住,死锁了
'''    

解决问题:使用相同的递归锁

import time
from threading import Thread, Lock, RLock

noodle_lock = fork_lock = RLock()  # 递归锁,也叫可重入锁

def eat1(name):
    noodle_lock.acquire()
    print('%s 抢到了面条' % name)
    fork_lock.acquire()
    print('%s 抢到了叉子' % name)
    print('%s 吃面' % name)
    fork_lock.release()
    noodle_lock.release()


def eat2(name):
    fork_lock.acquire()
    print('%s 抢到了叉子' % name)
    time.sleep(1)
    noodle_lock.acquire()
    print('%s 抢到了面条' % name)
    print('%s 吃面' % name)
    noodle_lock.release()
    fork_lock.release()


for name in ['哪吒', 'jack', 'tank']:
    t1 = Thread(target=eat1, args=(name,))
    t2 = Thread(target=eat2, args=(name,))
    t1.start()
    t2.start()
'''
哪吒 抢到了面条
哪吒 抢到了叉子
哪吒 吃面
哪吒 抢到了叉子
哪吒 抢到了面条
哪吒 吃面
jack 抢到了面条
jack 抢到了叉子
jack 吃面
jack 抢到了叉子
jack 抢到了面条
jack 吃面
tank 抢到了面条
tank 抢到了叉子
tank 吃面
tank 抢到了叉子
tank 抢到了面条
tank 吃面
就可以正常运行了
'''

信号量(简单)

1.在python并发编程中信号量相当于多把互斥锁(公共厕所)
2.在django中信号量的意思是到达某个条件自动触发其他操作(中间件)

我们之前学习的Lock产生的是单把锁,类似于单间厕所
信号量相当于一次性创建多把锁,类似于公共厕所
from threading import Thread, Lock, Semaphore
import time
import random


sp = Semaphore(5)  # 括号内部写几就是产生几把锁, 一次性产生五把锁


class MyThread(Thread):
    def run(self):
        sp.acquire()
        print(self.name)
        time.sleep(random.randint(1, 3))
        sp.release()


for i in range(20):
    t = MyThread()
    t.start()

event事件(简单)

子进程/子线程之间可以彼此等待彼此
eg:子A运行到某一个代码位置后发信号告诉子B开始运行

from threading import Thread, Event
import time

event = Event()  # 类似于造了一个红绿灯

def light():
    print('红灯亮着的 所有人都不能动')
    time.sleep(3)
    print('绿灯亮了 油门踩到底 给我冲!!!')
    event.set()  # 前面

def car(name):
    print('%s正在等红灯' % name)
    event.wait()  # 先等待,event.set()执行后才会执行这行代码
    print('%s加油门 飙车了' % name)


t = Thread(target=light)
t.start()
for i in range(20):
    t = Thread(target=car, args=('熊猫PRO%s' % i,))
    t.start()

线程队列

queue队列 :使用import queue,用法与进程Queue一样

"""
同一个进程下多个线程数据是共享的
为什么先同一个进程下还会去使用队列呢
因为队列是
    管道 + 锁
所以用队列还是为了保证数据的安全
"""

进程队列与线程队列

from multiprocessing import Queue
进程队列:Queue

import queue
线程队列:queue

"""
	进程Queue用于父进程与子进程(或同一父进程中多个子进程)间数据传递
    python自己的多个进程间交换数据或者与其他语言(如Java)进程queue就无能为力

    queue.Queue 的缺点是它的实现涉及到多个锁和条件变量,因此可能会影响性能和内存效率。
"""

线程队列有3种:

  1. 先进先出
  2. 先进后出
  3. 优先级队列

1.先进先出

# class queue.Queue(maxsize=0)
import queue

q = queue.Queue()
q.put('first')
q.put('second')
q.put('third')

print(q.get())
print(q.get())
print(q.get())
'''
结果(先进先出):
first
second
third
'''
  1. 先进后出
# class queue.LifoQueue(maxsize=0)
import queue

q = queue.LifoQueue()  # last in first out
q.put('first')
q.put('second')
q.put('third')

print(q.get())
print(q.get())
print(q.get())
'''
结果(后进先出):
third
second
first
'''

3.优先级队列

import queue

q = queue.PriorityQueue()
# put进入一个元组,元组的第一个元素是优先级(通常是数字,也可以是非数字之间的比较),数字越小优先级越高
q.put((5, 'a'))  # 元组里面第二个元素是具体的数据
q.put((15, 'b'))
q.put((10, 'c'))

print(q.get())
print(q.get())
print(q.get())
'''
结果(数字越小优先级越高,优先级高的优先出队):
(5, 'a')
(10, 'c')
(15, 'b')
'''

进程池和线程池(要掌握思路)

进程和线程不能无限制的创建。因为硬件的发展赶不上软件,有物理极限,如果我们在编写代码的过程中无限制的创建进程或者线程可能会导致计算机奔溃。

池:盛放更多个对象的,盛放更多的进程和线程。它降低程序的执行效率,但是保证了计算机硬件的安全。(池,面试时是加分项)
创建池的目的:节省资源,防止内存被占满的情况,另外就是也能提升效率,但是不能无限的开进程。

进程池:提前创建一个池子,这个池子里面先创建好一堆进程,超出则等待(设置上限).然后,只需要向池子里面丢任务就行。

线程池:提前创建一个池子,这个池子里面先创建好一堆线程,然后,只需要向池子里面丢任务就行。

连接池:MySQL中的连接池原理与上面两个一样

进程池执行单个任务

# 导入路径中得点代表:路径,点的左边一定是一个包
from concurrent.futures import ThreadPoolExecutor, ProcessPoolExecutor

def task(n, m, ):
    return n + m

# res=task(1, 2)  # 同步执行的
# print(res)  # 需要等待执行结果


def callback(res):  # 回调函数
    print(res)  # <Future at 0x14786aa45e0 state=finished returned int>
    print(res.result())  # 3


if __name__ == '__main__':
    # 1. 创建进程池
    # 创建好后就不会动了,永远是这5个线程,是固定的,不会再动态创建了
    p_pool = ProcessPoolExecutor(5)  # 创建一个池子,里面有5个进程
    # t_pool = ThreadPoolExecutor(5)  # 创建一个池子,里面有5个线程

    # 2. 执行任务,只需要往池子里面丢任务
    # p_pool.submit(task, 1, 2)  # 位置传参,有几个传几个
    # 异步操作,反馈机制, 异步回调机制
    p_pool.submit(task, n=1, m=2).add_done_callback(callback)


解析:
	进程池是异步调用,当一个进程结束之后,会自己主动调用回调函数callback(需要使用task返回值的函数就是回调函数),并将返回值对象当成参数传给回调函数的形参res,直接输出的话是一个返回值对象,使用对象.result()才会得到具体的返回值数值。

学习同步异步时,可以想到这个异步回调机制。
p_pool.submit(task, n=1, m=2).add_done_callback(callback)
任务提交之后不原地等待任务的结果,但是想要任务的结果时,可以添加一个反馈机制(异步回调机制)去获取返回值。
支付宝支付:也有同步回调、异步回调。但是本质是一样的(后期学习)

执行多个任务

from concurrent.futures import ThreadPoolExecutor, ProcessPoolExecutor


def task(n, m, ):  # 任务1
    return n + m


def task1(a, b, ):  # 任务2
    return {
        'name': 'ly',
        'a': a,
        'b': b
    }


def callback(res):  # 回调函数1
    print(res)  # <Future at 0x14786aa45e0 state=finished returned int>
    print(res.result())  # 3


def callback1(res):  # 回调函数2
    print(res)
    print(res.result())
    print(res.result()['name'])


if __name__ == '__main__':
    # 1. 创建进程池
    p_pool = ProcessPoolExecutor(5)  # 创建一个池子,里面有5个进程

    # 2. 执行任务,只需要往池子里面丢多个任务
    p_pool.submit(task, n=1, m=2).add_done_callback(callback)
    p_pool.submit(task1, a=10, b=20).add_done_callback(callback1)

# > 输出结果是:
<Future at 0x21d8d187c50 state=finished returned int>
3
<Future at 0x21d8d3cf898 state=finished returned dict>
{'name': 'ly', 'a': 10, 'b': 20}
ly

线程池爬取网页

import requests  # 第三方模块
# Request支持HTTP连接保持和连接池。作用:发送http请求,获取响应数据
from concurrent.futures import ThreadPoolExecutor, ProcessPoolExecutor


def get_page(url):
    # requests.get('http://www.baidu.com')  # 括号内部直接写网址
    res = requests.get(url)  # 爬取网页
    name = url.rsplit('/')[-1] + '.html'  # www.baidu.com.html
    return {'name': name, 'text': res.content}


def call_back(fut):
    print(fut.result()['name'])  # 输出名字
    with open(fut.result()['name'], 'wb') as f:  # 以name命名这个文件
        f.write(fut.result()['text'])  # 往文件中写爬出的内容


if __name__ == '__main__':
    pool = ThreadPoolExecutor(5)  # 开5个进程池
    urls = ['http://www.baidu.com', 'http://www.cnblogs.com', 'http://www.taobao.com']
    # 循环爬虫列表中的网址
    for url in urls:  
        pool.submit(get_page, url).add_done_callback(call_back)

标签:__,name,Thread,进程,线程,print
From: https://www.cnblogs.com/zjyao/p/17278917.html

相关文章

  • Java多线程(一篇从0讲透)
    多线程思维导图看天下:1.概述并行与并发并行:指两个或多个事件在同一时刻发生(同时发生)并发:指两个或多个事件在同一个时间段内发生。(交替执行)线程与进程进程:是指一个内存中运行的程序,每个进程都有一个独立的内存空间,一个应用程序可以同时运行多个进程记忆:进程的英文......
  • C#线程池
    C#中,线程池是一种用于执行异步操作的机制。线程池中维护着一组可重用的线程,我们可以将异步操作放到线程池中执行,从而避免阻塞当前线程。当异步操作完成后,线程池会将线程返回给线程池,以供下一次使用。如果线程池中的线程已经全部使用,而新的异步操作需要使用线程池中的线程时,线程池......
  • 调试freeradius时遇到的 线程池以及udp相关问题
    调试线程池过程中遇到了一个return和pthread_exit的问题;google一下发现右如下概念首先,return语句和pthread_exit()函数的含义不同,return的含义是返回,它不仅可以用于线程执行的函数,普通函数也可以使用;pthread_exit()函数的含义是线程退出,它专门用于结束某个线程的执行。在主......
  • 线程池
    //五个参数的构造函数publicThreadPoolExecutor(intcorePoolSize,intmaximumPoolSize,longkeepAliveTime,TimeUnitunit,BlockingQueueworkQueue)//六个参数的构造函数-1publicThreadPoolExecutor(intcorePoolSize,intmaximumPoolSize,longkeepAliveTime,TimeU......
  • Linux线程同步必知,常用方法揭秘!
    一、为什么要线程同步在Linux多线程编程中,线程同步是一个非常重要的问题。如果线程之间没有正确地同步,就会导致程序出现一些意外的问题,例如:竞态条件(RaceCondition):多个线程同时修改同一个共享变量,可能会导致不可预测的结果,因为线程的执行顺序是不确定的。死锁(Deadlock):当两个......
  • 多线程相关的问题(面试)
    1、线程创建的方式有哪几种1、继承Thread类;2、实现Runnable接口;3、实现Callable接口;4、使用Executor工具类创建线程池;5;使用ThreadPoolExecutor工具类创建线程池。2、线程的5种状态流转线程的状态如下状态:新建、就绪、运行、【阻塞】(等待(时间片用尽)、主动等待、挂起)、......
  • 结合 操作系统、Java多线程 学习并发编程
    为什么我们需要考虑并发?不考虑的话会出现什么问题?并发的多个程序(进程/线程)会对计算机资源进行争夺,如果不加以控制会出现混乱、严重影响程序运行效率,甚至错误首先是对CPU时间片的争夺对于多线程编程而言,由于创建线程后,线程的执行顺序是由调度程序控制的,也就是说各个线程的执行顺......
  • 多线程简介以及线程同步
    1.实现多线程1.1简单了解多线程【理解】是指从软件或者硬件上实现多个线程并发执行的技术。具有多线程能力的计算机因有硬件支持而能够在同一时间执行多个线程,提升性能。1.2并发和并行【理解】并行:在同一时刻,有多个指令在多个CPU上同时执行。并发:在同一时刻,有多个指令......
  • c++ 多线程编程std::thread, std::shared_mutex, std::unique_lock
    在C++11新标准中,可以简单通过使用thread库,来管理多线程,使用时需要#include<thread>头文件。简单用例如下:1std::thread(Simple_func);2std::threadt(Simple_func);3t.detach();第一行是直接启动一个新线程来执行Simple_func函数,而第二行先声明一个线程函数t(返回类型为......
  • 多线程队列接收
    packageorg.example.file.mult;//函数值接口@FunctionalInterfacepublicinterfaceFuncationCallback{voidcallback(Stringparam);} 回调接收 packageorg.example.file.mult;importjava.util.ArrayList;publicclassFuncationCallbackImpl{......