Python -- 进程和线程及进程的开发

进程和线程

线程

  • 在实现了线程的操作系统中,线程是操作系统能够进行运算调度的最小单位。它被包含在进程之中,是进程中的实际运作单位。一个程序的执行实例就是一个进程。

进程

  • 进程(Process)是计算机中的程序关于某数据集合上的一次运行活动,是系统进行资源分配和调度的基本单位,是操作系统结构的基础。

进程和程序的关系

  • 程序是源代码编译后的文件,而这些文件存放在磁盘上。当程序被操作系统加载到内存中,就是进程,进程中存放着指令和数据(资源),它也是线程的容器。

Linux进程有父进程、子进程,Windows的进程是平等关系。

  • 线程,有时被称为轻量级进程(Lightweight Process,LWP),是程序执行流的最小单元。
  • 一个标准的线程由线程ID,当前指令指针(PC)、寄存器集合和堆栈组成。
  • 在许多系统中,创建一个线程比创建一个进程快10-100倍。

进程、线程的理解

  • 现代操作系统提出进程的概念,每一个进程都认为自己独占所有的计算机硬件资源。
  • 进程就是独立的王国,进程间不可以随便的共享数据。
  • 线程就是省份,同一个进程内的线程可以共享进程的资源,每一个线程拥有自己独立的堆栈。

线程的状态

状态含义
就绪(Ready)线程能够运行,但在等待被调度。可能线程刚刚创建启动,或刚刚从阻塞中恢复,或者被其他线程抢占
运行(Running)线程正在运行
阻塞(Blocked)线程等待外部事件发生而无法运行,如I/O操作
终止(Terminated)线程完成,或退出,或被取消

在这里插入图片描述

Python中的进程和线程

  • 进程会启动一个解释器进程,线程共享一个解释器进程

Python的线程开发

  • Python的线程开发使用标准库threading
  • 进程靠线程执行代码,至少有一个主线程,其它线程是工作线程
  • 主线程是第一个启动的线程
  • 父线程:如果线程A中启动了一个线程B,A就是B的父线程
  • 子线程:B就是A的子线程

Thread类

# 签名
def __init__(self, group=None, target=None,
			 name=None,args=(), kwargs=None, *, daemon=None)
参数名含义
target线程调用的对象,就是目标函数
name为线程起个名字
args为目标函数传递实参,元组
kwargs为目标函数关键字传参,字典

线程启动

import threading

# 最简单的线程程序
def worker():
    print("I'm working")
    print("Fineshed")


t = threading.Thread(target=worker, name='worker') # 线程对象
t.start() # 启动
  • 通过threading.Thread创建一个线程对象,target是目标函数,可以使用name为线程指定名称
  • 但是线程没有启动,需要调用start方法
  • 线程之所以执行函数,是因为线程中就是要执行代码的,而最简单的封装就是函数,所以还是函数调用
  • 函数执行完,线程也就退出了

无线循环,不让线程退出

import threading
import time

def worker():
	while True:
		time.sleep(1)
    	print("I'm working")
    print("Fineshed")

t = threading.Thread(target=worker, name='worker') # 线程对象
t.start() # 启动

线程退出

  • Python没有提供线程退出的方法,线程在下面情况时退出
    1、线程函数内语句执行完毕
    2、线程函数中抛出未处理的异常
import threading
import time


def worker():
    count = 0
    while True:
        if count > 5:
            # raise RuntimeError(count) # 抛异常
            # return #函数返回
            break
        time.sleep(1)
        count += 1
        print("I'm working")
    print("Fineshed")


t = threading.Thread(target=worker, name='worker')
t.start()

print("===end===")
  • 执行结果
===end===
I'm working
I'm working
I'm working
I'm working
I'm working
I'm working
Fineshed

Python的线程没有优先级、没有线程组的概念,也不能被销毁、停止、挂起,那也就没有恢复、中断了。

线程的传参

import threading
import time

def add(x, y):
    print('{} + {} = {}'.format(x, y, x + y), threading.current_thread().ident)


t1 = threading.Thread(target=add, name='add1', args=(4, 5))
t1.start()
time.sleep(2)

t2 = threading.Thread(target=add, name='add2', args=(6,), kwargs={'y':7})
t2.start()
time.sleep(2)

t3 = threading.Thread(target=add, name='add3', kwargs={'x':8, 'y':9})
t3.start()

# 执行结果
4 + 5 = 9 227624
6 + 7 = 13 221152
8 + 9 = 17 8632

线程传参和函数传参没什么区别,本质上就是函数传参

threading的属性和方法

名称含义
current_thread()返回当前线程对象
main_thread()返回主线程对象
active_count()当前处于alive状态的线程个数
enumerate()返回所有活着的线程的列表,不包括已经终止的线程和未开始的线程
get_ident()返回当前线程的ID,非0整数

active_count、enumerate方法返回的值还包括主线程

import threading
import time

def showtreainfo():
    print(threading.current_thread(),threading.main_thread(),threading.active_count())

def worker():
    showtreainfo()
    for i in range(5):
        time.sleep(1)
        print('i am working')
    print('finished')

t = threading.Thread(target=worker, name='worker') # 线程对象
showtreainfo()
time.sleep(1)
t.start() # 启动

print('===enb===')

# 执行结果
<_MainThread(MainThread, started 272376)> <_MainThread(MainThread, started 272376)> 1
<Thread(worker, started 272352)> <_MainThread(MainThread, started 272376)> 2
===enb===
i am working
i am working
i am working
i am working
i am working
finished

Thread实例的属性和方法

名称含义
name只是一个名字,只是个标识,名称可以重名。getName()、setName()获取、设置这个名词
ident线程ID,它是非0整数。线程启动后才会有ID,否则为None。线程退出,此ID依旧可以访问。
此ID可以重复使用
is_alive()返回线程是否活着

注意:线程的name这是一个名称,可以重复;ID必须唯一,但可以在线程退出后再利用

import threading
import time

def worker():
    for i in range(5):
        time.sleep(1)
        print('i am working')
    print('finished')

t = threading.Thread(target=worker, name='worker') # 线程对象
print(t.name, t.ident)
time.sleep(1)
t.start() # 启动

print('===enb===')

while True:
    time.sleep(1)
    print('{} {} {}'.format(
        t.name, t.ident,'alive' if t.is_alive() else 'dead'))

    if not t.is_alive():
        print('{} restart'.format(t.name))
        t.start() # RuntimeError: threads can only be started once

在这里插入图片描述

名称含义
start()启动线程。每一个线程必须且只能执行该方法一次
run()运行线程函数
start方法
import threading
import time

def worker():
    for i in range(3):
        time.sleep(1)
        print('i am working')
    print('finished')

class MyThrad(threading.Thread):
    def start(self) -> None:
        print('start~~~~~')
        super().start()

    def run(self):
        print('run~~~~~~~')
        super().run()

t = MyThrad(target=worker, name='worker')
t.start()

# 运行结果
start~~~~~
run~~~~~~~
i am working
i am working
i am working
finished
run方法
import threading
import time

def worker():
    for i in range(3):
        time.sleep(1)
        print('i am working')
    print('finished')

class MyThrad(threading.Thread):
    def start(self) -> None:
        print('start~~~~~')
        super().start()

    def run(self):
        print('run~~~~~~~')
        super().run()

t = MyThrad(target=worker, name='worker')
# t.start()
t.run()
t.run() # 再次执行会抛AttributeError

在这里插入图片描述
start()方法会调用run()方法,而run()方法可以运行函数。

start和run的区别

在线程函数中,增加打印线程的名字的语句,看看能看到什么信息。

  • 执行start
import threading
import time

def worker():
    print(threading.enumerate()) # 增加这一句
    for i in range(3):
        time.sleep(1)
        print('i am working')
    print('finished')

class MyThrad(threading.Thread):
    def start(self) -> None:
        print('start~~~~~')
        super().start()

    def run(self):
        print('run~~~~~~~')
        super().run()

t = MyThrad(target=worker, name='worker')
t.start()
# t.run()

# 执行结果
start~~~~~
run~~~~~~~
[<_MainThread(MainThread, stopped 251576)>, <MyThrad(worker, started 216876)>]
i am working
i am working
i am working
finished
  • 执行run
import threading
import time

def worker():
    print(threading.enumerate()) # 增加这一句
    for i in range(3):
        time.sleep(1)
        print('i am working')
    print('finished')

class MyThrad(threading.Thread):
    def start(self) -> None:
        print('start~~~~~')
        super().start()

    def run(self):
        print('run~~~~~~~')
        super().run()

t = MyThrad(target=worker, name='worker')
# t.start()
t.run()

# 执行结果
run~~~~~~~
[<_MainThread(MainThread, started 208444)>]
i am working
i am working
i am working
finished
  • 使用start方法启动线程,启动了一个新的线程,名字叫做worker运行。
  • 但是使用run方法的,并没有启动新的线程,就是在主线程中调用了一个普通的函数而已。
  • 因此,启动线程请使用start方法,且对于这个线程来说,start方法只能调用一次。(设置_started属性实现)

多线程

顾名思义,多个线程,一个进程中如果有多个线程运行,就是多线程,实现一种并发。

import threading
import time

def worker():
    t = threading.current_thread()
    for i in range(5):
        time.sleep(1)
        print('i an working', t.name, t.ident)
    print('finished')

class MyTread(threading.Thread):
    def start(self) -> None:
        print('start~~~~~')
        super().start()

    def run(self):
        print('run~~~~~')
        super().run()

t1 = MyTread(target=worker, name='worker1')
t2 = MyTread(target=worker, name='worker2')
t1.start()
t2.start()

# 执行结果
start~~~~~
run~~~~~
start~~~~~
run~~~~~
i an working worker1 248696
i an working worker2 240448
i an working worker2 240448
i an working worker1 248696
i an working worker2 240448
i an working worker1 248696
i an working worker2 240448
i an working worker1 248696
i an working worker2 240448
i an working worker1 248696
finished
finished
  • 可以看到worker1和work2交替执行,改成run方法试试看
import threading
import time

def worker():
    t = threading.current_thread()
    for i in range(5):
        time.sleep(1)
        print('i an working', t.name, t.ident)
    print('finished')

class MyTread(threading.Thread):
    def start(self) -> None:
        print('start~~~~~')
        super().start()

    def run(self):
        print('run~~~~~')
        super().run()

t1 = MyTread(target=worker, name='worker1')
t2 = MyTread(target=worker, name='worker2')
# t1.start()
# t2.start()
t1.run()
t2.run()

# 执行结果
run~~~~~
i an working MainThread 6264
i an working MainThread 6264
i an working MainThread 6264
i an working MainThread 6264
i an working MainThread 6264
finished
run~~~~~
i an working MainThread 6264
i an working MainThread 6264
i an working MainThread 6264
i an working MainThread 6264
i an working MainThread 6264
finished
  • 没有开新的线程,这就是普通函数调用,所以执行完t1.run(),然后执行t2.run(),这里就不是多线程
  • 当使用start方法启动线程后,进程内有多个活动的线程并行的工作,就是多线程

一个进程中至少有一个线程,并作为程序的入口,这个线程就是主线程
一个进程至少有一个主线程
其他线程称为工作线程

线程安全

IPython中演示,python命令行、pycharm都不能演示出效果

import threading

def worker():
	for x in range(100):
		print("{} is ruinning.".format(threading.current_thread().name))

for x in range(1, 8): # 可以增加线程数
	name = "worker {}".format(x)
	t = threading.Thread(name=name,target=worker)
	t.start()

在这里插入图片描述

  • 线程不安全
    应该是一行行打印,但是很多字符串打在了一起,说明,print函数被打断了,被线程切换打断了。print函数分两步,第一步打印字符串,第二步打印换行符,就在这之间,发生了线程的切换。
    这说明print函数是线程不安全的
  • 线程安全
    线程执行一段代码,不会产生不确定的结果,那这段代码就是线程安全的

如果是这样,多线程编程的时候,print输出日志,不能保证一个输出一定后面立即换行了,怎么办?

  1. 不让print打印换行
import threading

def worker():
	for x in range(100):
		print("{} is ruinning.\n".format(threading.current_thread().name),end='')

for x in range(1, 8):
	name = "worker {}".format(x)
	t = threading.Thread(name=name,target=worker)
	t.start()

字符串是不可变的类型,它可以作为一个整体不可分割输出。end=''就不在让print输出换行

  1. 使用logging
    标准库里面的logging模块,日志处理模块,线程安全的,生成环境代码都使用logging
import threading
import logging

def worker():
    for x in range(100):
        logging.warning('{} is running.'.format(threading.current_thread().name))
        # print("{} is ruinning.\n".format(threading.current_thread().name),end='')

for x in range(1, 8):
    name = "worker {}".format(x)
    t = threading.Thread(name=name,target=worker)
    t.start()

daemon线程和non-daemon线程

注意:这里的daemon不是Linux中的守护进程

  • Python中,构造线程的时候,可以设置daemon属性,这个属性必须在start方法前设置好。
# 源码Thread的__init__方法中
if daemon is not None:
	self._daemonic = daemon # 用户设定bool值
else:
	self._daemonic = current_thread().daemon	
  • 线程daemon属性,如果设定就是用户的设置,否则就取当前线程的daemon值。
    主线程是non-daemon线程,即daemon = False。
class _MainThread(Thread):
    def __init__(self):
        Thread.__init__(self, name="MainThread", daemon=False)
import threading
import time

def foo():
    time.sleep(2)
    for i in range(5):
        print(i)

# 主线程是non-daemon线程
t = threading.Thread(target=foo, daemon=False)
t.start()

print('Main Thread Exits')

# 执行结果
Main Thread Exits
0
1
2
3
4

发现线程t依然执行,主线程已经执行完,但是一直等着线程t。
修改为 t = threading.Thread(target=foo, daemon=True)
程序立即结束了,主线程根本没有等线程t

名称含义
daemon属性表示线程是否是daemon线程,这个值必须在start()之前设置,否则引发RuntimeError异常
isDaemon()是否是daemon线程
setDaemon设置为daemon线程,必须在start方法之前设置
  • 观察下面代码主线程何时结束daemon线程
import threading
import time

def foo(name, timeout):
    time.sleep(timeout)
    print('{} working'.format(name))

# 主线程是non-daemon线程
t1 = threading.Thread(target=foo, args=('t1', 5), daemon=True) # 调换5和10看看效果
t1.start()

t2 = threading.Thread(target=foo, args=('t2', 8), daemon=False)
t2.start()

print('Main Thread Exits')

# 执行结果
Main Thread Exits
t1 working
t2 working

上例说明,如果除主线程之外还有non-daemon线程的时候,主线程退出时,也不会杀掉所有daemon线程,直到所有non-daemon线程全部结束,如果还有daemon线程,主线程需要退出(主线程退出也可以理解为最后一个non-daemon线程也要退出了),会结束所有daemon线程,程序退出。

总结
  • 线程具有一个daemon属性,可以手动设置为True或False,也可以不设置,则取默认值None。
  • 如果不设置daemon,就取当前线程的daemon来设置它。
  • 主线程是non-daemon线程,即daemon = False。
  • 从主线程创建的所有线程的不设置daemon属性,则默认都是daemon = False,也就是non-daemon线程。
  • Python程序在没有活着的non-daemon线程运行时,程序退出,也就是除主线程之外剩下的只能都是daemon线程,主线程才能退出,否则主线程就只能等待。

join方法

import threading
import time

def foo(name, timeout):
    time.sleep(timeout)
    print('{} working'.format(name))

t1 = threading.Thread(target=foo, args=('t1', 5), daemon=True) # 调换5和10看看效果
t1.start()
t1.join()# 设置join,取消join对比一下

print('Main Thread Exits')

# 执行结果
t1 working
Main Thread Exits
  • 使用了join方法后,daemon线程执行完了,主线程才退出了
  • 如果取消join方法,主线程执行完直接退出
import threading
import time

def foo(name, timeout):
    time.sleep(timeout)
    print('{} working'.format(name))

t1 = threading.Thread(target=foo, args=('t1', 5), daemon=True) # 调换5和10看看效果
t1.start()
t1.join(2)
print('~~~~~~~~~~~')
t1.join(2)
print('~~~~~~~~~~~')

print('Main Thread Exits')

# 执行结果
~~~~~~~~~~~
~~~~~~~~~~~
Main Thread Exits
  • join(timeout=None),是线程的标准方法之一
  • 一个线程中调用另一个线程的join方法,调用者将被阻塞,直到被调用线程终止
  • 一个线程可以被join多次
  • timeout参数指定调用者等待多久,没有设置超时,就一直等到被调用线程结束
  • 调用谁的join方法,就是join谁,就要等谁

daemon线程应用场景

  • 简单来说就是,本来并没有 daemon thread,为了简化程序员的工作,让他们不用去记录和管理那些后台线程,创造了一个 daemon thread 的概念。这个概念唯一的作用就是,当你把一个线程设置为 daemon,它可以会随主线程的退出而退出
  • 主要应用场景有:
  1. 后台任务。如发送心跳包、监控,这种场景最多。
  2. 主线程工作才有用的线程。如主线程中维护这公共的资源,主线程已经清理了,准备退出,而工作线程使用这些资源工作也没有意义了,一起退出最合适
  3. 随时可以被终止的线程
    如果主线程退出,想所有其它工作线程一起退出,就使用daemon=True来创建工作线程
    比如,开启一个线程定时判断WEB服务是否正常工作,主线程退出,工作线程也没有必须存在了,应该随着主线程退出一起退出。这种daemon线程一旦创建,就可以忘记它了,只用关心主线程什么时候退出就行了
  • daemon线程,简化了程序员手动关闭线程的工作
    如果在non-daemon线程A中,对另一个daemon线程B使用了join方法,这个线程B设置成daemon就没有什么意义了,因为non-daemon线程A总是要等待B
    如果在一个daemon线程C中,对另一个daemon线程D使用了join方法,只能说明C要等待D,主线程退出,C和D不管是否结束,也不管它们谁等谁,都要被杀掉
举例
import threading
import time

def worker1(name):
    while True:
        time.sleep(1)
        print('{} working'.format(name), threading.current_thread().isDaemon())

def worker2(name):
    current = threading.current_thread()
    print("{} s daemon = {}".format(name, current.isDaemon()))
    t2 = threading.Thread(target=worker1, args=('t2',)) # 隐式 daemon=True,继承自t1
    t2.start()

t1 = threading.Thread(target=worker2, args=('t1',), daemon=True)
t1.start()

time.sleep(4)
print('Main Thread Exits')

# 执行结果
t1 s daemon = True
t2 working True
t2 working True
t2 working True
Main Thread Exits

上例,只要主线程要退出,2个工作线程都结束
使用join,让线程不结束

import threading
import time

def worker1(name):
    while True:
        time.sleep(1)
        print('{} working'.format(name), threading.current_thread().isDaemon())

def worker2(name):
    current = threading.current_thread()
    print("{} s daemon = {}".format(name, current.isDaemon()))
    t2 = threading.Thread(target=worker1, args=('t2',))
    t2.start()
    t2.join()

t1 = threading.Thread(target=worker2, args=('t1',), daemon=True)
t1.start()
t1.join()


time.sleep(4)
print('Main Thread Exits')

# 执行结果
t1 s daemon = True
t2 working True
t2 working True
······

threading.local类

import threading
import time

def worker():
    x = 0
    for i in range(100):
        time.sleep(0.0001)
        x += 1
    print(threading.current_thread(), x)

for i in range(10):
    threading.Thread(target=worker).start()

# 执行结果
<Thread(Thread-2, started 246588)> 100
<Thread(Thread-3, started 225832)> 100
<Thread(Thread-8, started 227960)> 100
<Thread(Thread-4, started 270992)> 100
<Thread(Thread-10, started 219460)> 100
<Thread(Thread-9, started 217800)> 100
<Thread(Thread-7, started 246036)> 100
<Thread(Thread-6, started 216876)> 100
<Thread(Thread-1, started 21356)> 100
<Thread(Thread-5, started 228668)> 100

上例使用多线程,每个线程完成不同的计算任务。
x是局部变量,可以看出每一个线程的x是独立的,互不干扰的

  • 改造成全局变量
import threading
import time

class A:
    def __init__(self):
        self.x = 0

# 全局对象
global_data = A()


def worker():
    global_data.x = 0
    for i in range(100):
        time.sleep(0.0001)
        global_data.x += 1
    print(threading.current_thread(), global_data.x)

for i in range(10):
    threading.Thread(target=worker).start()

# 执行结果
<Thread(Thread-2, started 264624)> 980
<Thread(Thread-1, started 271244)> 984
<Thread(Thread-4, started 224060)> 985
<Thread(Thread-7, started 220284)> 989
<Thread(Thread-6, started 240120)> 990
<Thread(Thread-10, started 247876)> 994
<Thread(Thread-8, started 272280)> 996
<Thread(Thread-3, started 271524)> 997
<Thread(Thread-9, started 229516)> 998
<Thread(Thread-5, started 251604)> 999

上例虽然使用了全局对象,但是线程之间互相干扰,导致了不期望的结果。
如何既使用全局对象,还能保持每个线程使用不同的数据

  • python提供threading.local 类,将这个类实例化得到一个全局对象,但是不同的线程使用这个对象存储的数据
    其他线程看不见
import threading
import time

# 全局对象
global_data = threading.local()


def worker():
    global_data.x = 0
    for i in range(100):
        time.sleep(0.0001)
        global_data.x += 1
    print(threading.current_thread(), global_data.x)

for i in range(10):
    threading.Thread(target=worker).start()

# 执行结果
<Thread(Thread-1, started 217928)> 100
<Thread(Thread-9, started 223172)> 100
<Thread(Thread-7, started 251612)> 100
<Thread(Thread-3, started 271392)> 100
<Thread(Thread-10, started 191600)> 100
<Thread(Thread-2, started 269056)> 100
<Thread(Thread-6, started 253464)> 100
<Thread(Thread-8, started 222456)> 100
<Thread(Thread-4, started 221908)> 100
<Thread(Thread-5, started 265244)> 100

结果显示和使用局部变量的效果一样。

  • 再看threading.local的例子
import threading
import time

X = 'abc'
global_data = threading.local() # 注意这个对象所处的线程
global_data.x = 100

print(global_data, type(global_data), global_data.x)

def worker():
    print(X)
    print(global_data)
    # print(global_data.x) # AttributeError: '_thread._local' object has no attribute 'x'
    print('in worker')

worker()
print('-' * 30)
threading.Thread(target=worker).start()

# 执行结果
<_thread._local object at 0x0000022C6D77E938> <class '_thread._local'> 100
abc
<_thread._local object at 0x0000022C6D77E938>
in worker
------------------------------
abc
<_thread._local object at 0x0000022C6D77E938>
in worker
  • 再另外一个线程中执行print(global_data.x)会抛AttributeError: '_thread._local' object has no attribute 'x'错误,
    global_data打印没有出错,说明看到global_data,而global_data中的x看不到,所以这个x不能跨线程
  • threading.local类构建了一个大字典,存放所有线程相关的字典,定义如下:
    { id(Thread) -> (ref(Thread), thread-local dict) }
    每一线程实例的id为key,元组为value
    value中2部分为,线程对象引用,每个线程自己的字典

本质

  • 运行时,threading.local实例处在不同的线程中,就从大字典中找到当前线程相关键值对中的字典,覆盖threading.local实例的 __dict__
  • 这样就可以在不同的线程中,安全地使用线程独有的数据,做到了线程间数据隔离,如同本地变量一样安全
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
C语言是一种广泛使用的编程语言,它具有高效、灵活、可移植性强等特点,被广泛应用于操作系统、嵌入式系统、数据库、编译器等领域的开发。C语言的基本语法包括变量、数据类型、运算符、控制结构(如if语句、循环语句等)、函数、指针等。在编写C程序时,需要注意变量的声明和定义、指针的使用、内存的分配与释放等问题。C语言中常用的数据结构包括: 1. 数组:一种存储同类型数据的结构,可以进行索引访问和修改。 2. 链表:一种存储不同类型数据的结构,每个节点包含数据和指向下一个节点的指针。 3. 栈:一种后进先出(LIFO)的数据结构,可以通过压入(push)和弹出(pop)操作进行数据的存储和取出。 4. 队列:一种先进先出(FIFO)的数据结构,可以通过入队(enqueue)和出队(dequeue)操作进行数据的存储和取出。 5. 树:一种存储具有父子关系的数据结构,可以通过中序遍历、前序遍历和后序遍历等方式进行数据的访问和修改。 6. 图:一种存储具有节点和边关系的数据结构,可以通过广度优先搜索、深度优先搜索等方式进行数据的访问和修改。 这些数据结构在C语言中都有相应的实现方式,可以应用于各种不同的场景。C语言中的各种数据结构都有其优缺点,下面列举一些常见的数据结构的优缺点: 数组: 优点:访问和修改元素的速度非常快,适用于需要频繁读取和修改数据的场合。 缺点:数组的长度是固定的,不适合存储大小不固定的动态数据,另外数组在内存中是连续分配的,当数组较大时可能会导致内存碎片化。 链表: 优点:可以方便地插入和删除元素,适用于需要频繁插入和删除数据的场合。 缺点:访问和修改元素的速度相对较慢,因为需要遍历链表找到指定的节点。 栈: 优点:后进先出(LIFO)的特性使得栈在处理递归和括号匹配等问题时非常方便。 缺点:栈的空间有限,当数据量较大时可能会导致栈溢出。 队列: 优点:先进先出(FIFO)的特性使得
该资源内项目源码是个人的课程设计、毕业设计,代码都测试ok,都是运行成功后才上传资源,答辩评审平均分达到96分,放心下载使用! ## 项目备注 1、该资源内项目代码都经过测试运行成功,功能ok的情况下才上传的,请放心下载使用! 2、本项目适合计算机相关专业(如计科、人工智能、通信工程、自动化、电子信息等)的在校学生、老师或者企业员工下载学习,也适合小白学习进阶,当然也可作为毕设项目、课程设计、作业、项目初期立项演示等。 3、如果基础还行,也可在此代码基础上进行修改,以实现其他功能,也可用于毕设、课设、作业等。 下载后请首先打开README.md文件(如有),仅供学习参考, 切勿用于商业用途。 该资源内项目源码是个人的课程设计,代码都测试ok,都是运行成功后才上传资源,答辩评审平均分达到96分,放心下载使用! ## 项目备注 1、该资源内项目代码都经过测试运行成功,功能ok的情况下才上传的,请放心下载使用! 2、本项目适合计算机相关专业(如计科、人工智能、通信工程、自动化、电子信息等)的在校学生、老师或者企业员工下载学习,也适合小白学习进阶,当然也可作为毕设项目、课程设计、作业、项目初期立项演示等。 3、如果基础还行,也可在此代码基础上进行修改,以实现其他功能,也可用于毕设、课设、作业等。 下载后请首先打开README.md文件(如有),仅供学习参考, 切勿用于商业用途。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值