首页 > 其他分享 >线程

线程

时间:2022-11-21 18:59:20浏览次数:390  
标签:name start 线程 time print import

线程

线程理论

进程
    进程其实是资源单位,表示的是一块内存空间
线程
    线程是执行单位,表示真正的代码指令
一个进程内部至少含有一个线程

1.一个进程内可以开设多个线程
2.同一个进程内的多个线程数据共享  # 进程间数据隔离,同进程下的线程数据共享
3.创建进程与线程的区别
    创建进程的消耗要远大于线程  # 创建一个进程就等于开辟了一块内存空间,线程不用

创建线程使用threading模块下的Thread类,使用方法与创建进程一致

创建线程的两种方式

from threading import Thread
from multiprocessing import Process
import time

# 方式一:函数
def task(name):
    print(f'{name} is running')
    time.sleep(0.1)
    print(f'{name} is over')

t = Thread(target=task, args=('jason',))
t.start()
print('主线程')

# 方式二:类
class MyThread(Thread):
    def run(self):
        print('run is running')
        time.sleep(1)
        print('run is over')

obj = MyThread()
obj.start()
print('主线程')

线程的诸多特性

1.join方法
2.同进程内多个线程数据共享
3.current_thread()  # 当前线程的的线程变量
4.active_count()  # 返回正在运行的线程数量

GIL全局解释锁

官方文档对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
这段英文提取出来的内容有
1.在CPython解释器中存在全局解释器锁简称GIL
	python解释器有很多类型
		CPython JPython PyPython (常用的是CPython解释器) 
CPython解释器就是用C编写的Python解释器
2.GIL本质也是一把互斥锁 用来阻止同一个进程内多个线程同时执行(重要)
3.GIL的存在是因为CPython解释器中内存管理不是线程安全的(垃圾回收机制)
    由于垃圾回收机制的存在使得线程使用的数据可能会被提前回收掉

验证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)
for t in t_list:
    t.join()
print(num)

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)

python多线程是否有用

这个是需要分情况的
主要看是单CPU还是多CPU,程序是IO密集型(代码有IO操作)还是计算密集型(代码没有IO)
1.单个CUP
    IO密集型
        多进程
            申请额外的内存空间,需要消耗更多的资源
        多线程
            资源消耗相对较少,通过多道技术
        多线程有优势
    计算密集型
        多进程
            申请额外的空间 消耗更多的资源(总耗时+申请空间+拷贝代码+切换)
        多线程
            消耗资源相对较少 通过多道技术(总耗时+切换)
        多线程有优势
2.多个CPU
	IO密集型
   		多进程
         总耗时(单个进程的耗时+IO+申请空间+拷贝代码)
       多线程
    	  总耗时(单个进程的耗时+IO)
       多线程有优势
	计算密集型
    	多进程
       	  总耗时(单个进程的耗时)
    	多线程
          总耗时(多个进程的综合)
       多进程完胜
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))  # 获取总的耗时


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


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

死锁现象


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()

以上面这段程序为例,创建了十个线程,第一个线程执行完func1以后,A锁B锁都释放了,这时func2执行了抢到了B锁,但是func2停顿了一秒,导致A锁被后面线程的func1抢去了,然后func2结束停顿后要抢A锁,后面抢到了A锁的线程也要抢B锁,偏偏A锁B锁都被抢了,于是程序就在这里尬住了,这个就是死锁现象

信号量

在python并发编程中信号量相当于多把互斥锁(公共厕所)
	
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事件

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()

进程池与线程池

因为硬件的限制,进程和线程不能无限制的创建
进程池
	提前创建好固定数量的进程供后续程序的调用 超出则等待
线程池
	提前创建好固定数量的线程供后续程序的调用 超出则等待
from concurrent.futures import ProcessPoolExecutor, ThreadPoolExecutor
import os
import time
import random
from threading import current_thread

# 1.产生含有固定数量线程的线程池
# pool = ThreadPoolExecutor(10)
pool = ProcessPoolExecutor(5)


def task(n):
    print('task is running')
    # time.sleep(random.randint(1, 3))
    # print('task is over', n, current_thread().name)
    # print('task is over', os.getpid())
    return '我是task函数的返回值'


def func(*args, **kwargs):
    print('from func')

if __name__ == '__main__':
    # 2.将任务提交给线程池即可
    for i in range(20):
        res = pool.submit(task, 123)  # 朝线程池提交任务
        print(res.result())  # 不能直接获取
        pool.submit(task, 123).add_done_callback(func)

协程

进程:资源单位
线程:执行单位
协程:单线程下实现并发(效率极高)
	在代码层面欺骗CPU 让CPU觉得我们的代码里面没有IO操作
	实际上IO操作被我们自己写的代码检测 一旦有 立刻让代码执行别的
	(该技术完全是程序员自己弄出来的 名字也是程序员自己起的)
		核心:自己写代码完成切换+保存状态
import time
from gevent import monkey;

monkey.patch_all()  # 固定编写 用于检测所有的IO操作(猴子补丁)
from gevent import spawn


def func1():
    print('func1 running')
    time.sleep(3)
    print('func1 over')


def func2():
    print('func2 running')
    time.sleep(5)
    print('func2 over')


if __name__ == '__main__':
    start_time = time.time()
    # func1()
    # func2()
    s1 = spawn(func1)  # 检测代码 一旦有IO自动切换(执行没有io的操作 变向的等待io结束)
    s2 = spawn(func2)
    s1.join()
    s2.join()
    print(time.time() - start_time)  # 8.01237154006958   协程 5.015487432479858

使用协程实现并发

import socket
from gevent import monkey;monkey.patch_all()  # 固定编写 用于检测所有的IO操作(猴子补丁)
from gevent import spawn


def communication(sock):
    while True:
        data = sock.recv(1024)
        print(data.decode('utf8'))
        sock.send(data.upper())


def get_server():
    server = socket.socket()
    server.bind(('127.0.0.1', 8080))
    server.listen(5)
    while True:
        sock, addr = server.accept()  # IO操作
        spawn(communication, sock)

s1 = spawn(get_server)
s1.join()

如何不断的提升程序的运行效率
	多进程下开多线程 多线程下开协程

标签:name,start,线程,time,print,import
From: https://www.cnblogs.com/zyg111/p/16912320.html

相关文章

  • 线程和进程和锁
    目录今日内容概要今日内容详细多进程实现TCP服务端并发互斥锁代码实操线程理论创建线程的两种方式线程的诸多特性GIL全局解释器锁验证GIL的存在GIL与普通互斥锁python多线......
  • 多线程、GIL全局解释器锁、协程
    目录多进程实现TCP服务端并发互斥锁代码实操线程理论创建线程的两种方式线程的诸多特性GIL全局解释器锁验证GIL的存在GIL与普通互斥锁python多线程是否有用死锁现象信号量e......
  • 线程理论及threading模块
    线程理论及threading模块线程理论线程和进程在使用方式和调度策略上十分的相似,两者的本质区别在于进程:进程是资源单位,表示一块内存空间线程:线程是执行单位,真正的执行......
  • 万字详解 Java 线程安全,面试必备!
    来源:blog.csdn.net/u014454538/article/details/985158071.Java中的线程安全Java线程安全:狭义地认为是多线程之间共享数据的访问。Java语言中各种操作共享的数据有5种......
  • 15.多线程并发在电商系统下的追本溯源(2)
                                                         ......
  • WPF 后台线程操作界面元素不卡UI界面线程 Dispatcher
    经常要考虑的,后台的耗时操作不要卡死主界面的问题。<StackPanelVerticalAlignment="Center"><Labelx:Name="lblHello">欢迎你光临WPF的世界!</Label><ButtonName......
  • C++多线程
    c++多线程多线程其实非常简单多线程是多任务处理的一种特殊形式,多任务处理允许让电脑同时运行两个或两个以上的程序。一般情况下,两种类型的多任务处理:基于进程和基于线程......
  • 饿汉模式,懒汉模式线程安全问题
    饿汉模式:上来不管三七二十一直接创建对象再说。饿汉模式创建方式:1.先创建一个私有的构造函数(防止其它地方直接实例化)2.定义私有变量3.提供公共的获取实例的方法pub......
  • 线程的私有空间
    线程内部的全局变量如果需要在一个线程内部的各个函数调用都能访问、但其它线程不能访问的变量,这就需要新的机制来实现,我们称之为Staticmemorylocaltoathread(线程局......
  • 【Java】JDK5.0新增的创建多线程的方式:实现Callable接口,使用线程池
    1.实现Callable接口方式和实现Runnable接口相比call()可以有返回值。call()可以抛出异常,被外面的操作捕获,获取异常信息。Callable是支持泛型的。实现Callable接口......