了解python之进程与线程

2023-05-16

了解python之进程与线程

// 本文虽然叫作 “了解python进程与线程”, 但还是有点难度的。 可以先查阅另外一篇文字,快速入门。 Python快速入门多线程与多进程

 

1.进程

进程(Process,有时也被称为重量级进程)是程序的一次执行,每个进程都有自己的地址空间、内存、数据栈以及记录运行轨迹的辅助数据,操作系统管理运行的所有进程,并为这些进程公平分配时间。进程可以通过fork和spawn操作完成其他任务。因为各个进程都有自己的内存空间和数据栈等,所以只能使用进程间通信(IPC),而不能直接共享信息。

 

2.线程:

线程(Thread,有时被称为轻量级的进程)跟进程有些相似,不同的是所有线程运行在同一个进程中,共享运行环境。线程有开始、顺序执行和结束3个部分,有一个自己的指令指针,记录运行到什么地方。一个进程中的各个线程之间共享同一片数据空间,所以线程之间可以比进程之间更好更方便的共享数据和相互通信。

线程一般是并发执行的,正是由于这种并行和数据共享的机制,使得多个任务的合作变的可能。实际上,在单CPU系统中,真正的并发并不可能,每个线程会被安排每次只运行一小会,然后就把CPU让出来,让其他线程运行。

在进程的整个运行过程中,每个线程都只做自己的事,需要在跟其他线程共享运行结果。多个线程共享同一片数据不是完全没有危险的,由于数据访问顺序不一样,因此可能会导致数据结果不一致的问题,这叫做竞态条件。大多数线程库都带有一系列同步原语,用于控制线程的执行和数据访问。

 

每个线程都有他自己的一组CPU寄存器,称为线程的上下文,该上下文反映了线程上次运行该线程的CPU寄存器的状态。 指令指针和堆栈指针寄存器是线程上下文中两个最重要的寄存器,线程总是在进程得到上下文中运行的,这些地址都用于标志拥有线程的进程地址空间中的内存。

  • 线程可以被抢占(中断)。
  • 在其他线程正在运行时,线程可以暂时搁置(也称为睡眠) -- 这就是线程的退让。

线程可以分为:

  • 内核线程:由操作系统内核创建和撤销。
  • 用户线程:不需要内核支持而在用户程序中实现的线程。

Python3 线程中常用的两个模块为:

  • _thread
  • threading(推荐使用)

thread 模块已被废弃。用户可以使用 threading 模块代替。所以,在 Python3 中不能再使用"thread" 模块。为了兼容性,Python3 将 thread 重命名为 "_thread"。

3.多线程和多进程


什么叫多任务?
简单的说就是系统可以同时运行多个任务。比如,一边用浏览器上网,一边听音乐,一边聊天,这就是多任务。假如现在已经有三个任务在运行了,查看任务管理器,可以看到很多任务在后台运行,只是在桌面没有显示。对于操作系统来说,一个任务就是一个进程,开启多个任务就是多进程。

有些进程不止可以做一件事,比方说word可以是同时打字、拼写检查、打印等。在一个进程内部,要同时做多件事,就需要同时运行多个线程

 

多线程类似于同时执行多个不同的程序,多线程运行有以下几个优点:
1)使用线程可以把占据长时间的程序中的任务放到后台去处理
2)用户界面可以更加吸引人,比如用户单击一个按钮,用于触发某些事件的处理,可以弹出一个进度条显示处理的进度
3)程序的运行速度可能加快
4)在实现一些等待任务(如用户输入、文件读写、网络收发数据等)的时候,使用多线程更加有用。在这种情况下,我们可以释放一些珍贵的资源(如内存占用等)。

 

线程在执行过程中与进程还是有区别的。每个独立的线程有一个程序运行的入口、顺序执行序列和程序的出口。但是线程不能独立执行,必须依存在进程中,由进程提供多个线程执行控制。

由于每个进程至少要干一件事,因此一个进程至少有一个线程。多线程的执行方式和多进程是一样的,也是由操作系统在多个线程之间相互切换,让每个线程都短暂交互运行,看起来就像同时执行一样。同时执行多线程需要多核CPU才能实现。

 

如果要同时执行多个任务,怎们办?

  • 第一种方法:启动多个进程,每个进程虽然只有一个线程,但是多个进程可以一起执行多个任务。
  • 第二种方法:启动一个进程,在一个进程内启动多个线程,这样多个线程也可以一起执行多个任务
  • 第三种方法:启动多个进程,每个进程在启动多个线程,这样同时执行的任务就更多了,这种模式过于复杂,实际上很少采用。

同时执行多个任务时,各个任务之间需要相互通信和协调,有时候任务1需要暂停,等待任务2完成后才能继续执行;有时任务3和任务4不能同时执行。多进程、多线程程序的复杂度远远高于前面的单进程、单线程的程序。

综上所述:

  • 多线程是多个相关联的线程的组合,多进程是多个相互独立的进程的组合。
  • 线程是最小的执行单元,进程至少由一个线程组成。

 

4.并发和并行

一个程序在计算机中运行,其底层是处理器通过运行一条条的指令来实现的。

并发,英文叫作 concurrency它是指同一时刻只能有一条指令执行,但是多个线程的对应的指令被快速轮换地执行。比如一个处理器,它先执行线程 A 的指令一段时间,再执行线程 B 的指令一段时间,再切回到线程 A 执行一段时间。

由于处理器执行指令的速度和切换的速度非常非常快,人完全感知不到计算机在这个过程中有多个线程切换上下文执行的操作,这就使得宏观上看起来多个线程在同时运行。但微观上只是这个处理器在连续不断地在多个线程之间切换和执行,每个线程的执行一定会占用这个处理器一个时间片段,同一时刻,其实只有一个线程在执行。

 

并行,英文叫作 parallel它是指同一时刻,有多条指令在多个处理器上同时执行,并行必须要依赖于多个处理器。不论是从宏观上还是微观上,多个线程都是在同一时刻一起执行的。

并行只能在多处理器系统中存在,如果我们的计算机处理器只有一个核,那就不可能实现并行。而并发在单处理器和多处理器系统中都是可以存在的,因为仅靠一个核,就可以实现并发

 

举个例子,比如系统处理器需要同时运行多个线程。如果系统处理器只有一个核,那它只能通过并发的方式来运行这些线程。如果系统处理器有多个核,当一个核在执行一个线程时,另一个核可以执行另一个线程,这样这两个线程就实现了并行执行,当然其他的线程也可能和另外的线程处在同一个核上执行,它们之间就是并发执行。具体的执行方式,就取决于操作系统的调度了。

 

5.全局解释器锁(GIL)

python虚拟机的访问由全局解释器锁控制,这个锁能保证同一个时刻只有一个线程运行。

在多线程环境中,python虚拟机按照以下方式执行:
1)设置GIL
2)切换到一个线程运行
3)运行指定数量的字节编码指令或者线程主动让出控制(可以调用time.sleep())
4)把线程设置成睡眠状态
5)解锁GIL
6)再次重复以上所有步骤

在调用外部代码时,GIL将被锁定。直到这个函数结束为止,编写扩展的程序员可以主动解锁GIL。

简言之,无论你启动多少个线程,你有多少个cpu,python在执行的时候在同一时刻只允许同一个线程运行。

6.退出线程

当一个线程结束计算,它就退出了。线程可以调用_thread.exit()等退出函数,也可以使用python退出进程的标准方法(sys.exit()或者抛出一个SystemExit异常),不能直接kill掉一个线程。

不建议使用_thread模块,最重要的原因是:当主线程退出时,其他线程如果没有被清除就会退出。另一个模块threading能确保所有的重要的子线程都退出后,进程才会结束。
 

python中的线程模块:
python提供了几个用于多线程编程的模块,包括_thread、threading和Queue等。_thread和threading模块允许程序员创建和管理线程。_thread模块提供了基本线程和锁的支持,threading提供了更高级别、功能更强的线程管理功能。Queue模块允许允许用户创建一个可以用于多线程之间共享数据的队列数据结构。

 

为什么建议不使用_thread模块?

  • threading模块更先进,对线程的支持更完善,而且使用_thread模块里的属性有可能跟threading冲突;
  • 低级别的_thread模块同步原语很少,threading很多;
  • _thread模块在主线程结束时,所有线程都会被强制结束,没有警告也不会有正常的清除工作,至少threading模块能确保重要的子线程退出后进程才退出;

 

多线程的缺点:
由于GIL的存在,python多线程只能有一个线程执行,无法利用多核cpu实现并行执行的特点。由于线程的切换需要时间的,多线程使用不当程序的执行速度还不如单线程。

 

7.python的_thread模块

python中调用_thread模块中的start_new_thread()函数产生一个新的线程。_thread语法如下:

_thread.start_new_thread(function,args[,kwargs])

参数说明:

  • function-线程函数
  • args-传递给线程函数的参数,必须是元组(tuple)
  • kwargs-可选参数

_thread模块除了产生线程外,还提供基本同步数据结构锁对象lock object,也叫做原语锁、简单锁、互斥锁、互斥量、二值信号量)。同步原语与线程管理是密不可分的。

 

_thread模块的主要方法:

  • _thread.start_new_thread(function,args[,kwargs])
  • _thread.allocate_lock()                                    #分配锁对象
  • _thread.exit()                                                   #退出线程
  • lock.acquire(waitflag=1,timeout=1)                 #获取锁对象
  • lock.locked()                                                   #如果获取了锁对象,返回true,否则返回false
  • lock.release()                                                 #释放锁
  • _thread.LockType()                                       #锁对象的类型
  • _thread.get_ident()                                       #获取线程标识符
  • _thread.TIMEOUT_MAX                              #locak.acquire的最大时间,超过这个时间引发OverflowError
  • _thread.interrupt_main()                              #引发主线程keyboardInterrupt错误,子线程可以用这个函数终止主线程

 

例1,多线程实例

import _thread
import time

#为线程定义的函数
def print_time(threadName,delay):
    count=0
    while count <5:
        time.sleep(delay)
        count +=1   #等价于count=count+1
        print('%s: %s' % (threadName,time.ctime(time.time())))

#创建两个线程
try:
    _thread.start_new_thread(print_time,("Thread-1",2))
    _thread.start_new_thread(print_time,('Thread-2',6))
except:
    print('Error: 线程无法启动')

while 1:
    pass


##输出结果:
Thread-1: Wed May 26 09:25:49 2021
Thread-1: Wed May 26 09:25:51 2021
Thread-2: Wed May 26 09:25:53 2021
Thread-1: Wed May 26 09:25:53 2021
Thread-1: Wed May 26 09:25:55 2021
Thread-1: Wed May 26 09:25:57 2021
Thread-2: Wed May 26 09:25:59 2021
Thread-2: Wed May 26 09:26:05 2021
Thread-2: Wed May 26 09:26:11 2021
Thread-2: Wed May 26 09:26:17 2021

 

例2:4个线程分别执行loop函数,中间等待nsec秒,nsec分别是4,2,3,5

from time import ctime
from time import sleep
import _thread

loops = [4,2,3,5]

def loop(nloop,nsec,lock):   #nloop表示的是标识,nsec表示的是睡眠时间,lock表示的是锁对象
    print('start loop',nloop,'at:',ctime())
    sleep(nsec)
    print('done loop',nloop,'at:',ctime())
    lock.release()                        #释放锁

def main():
    print('start at:',ctime())
    locks=[]
    nloops=range(len(loops))             #nloops=range(4)
    for i in nloops:
           lock=_thread.allocate_lock()  #分配锁对象
           lock.acquire()                #获取锁对象
           locks.append(lock)
    print(locks)
    #启动一个线程
    for i in nloops:
        _thread.start_new_thread(loop,(i,loops[i],locks[i]))    #启动一个新线程

    #等待所有的锁被释放
    for i in nloops:
        while(locks[i].locked()):
            pass
    print('all done at:', ctime())

if __name__ == '__main__':
    main()


##输出结果:
start at: Wed May 26 09:31:06 2021
[<locked _thread.lock object at 0x000001FC4951C750>, <locked _thread.lock object at 0x000001FC49583A20>, <locked _thread.lock object at 0x000001FC49583AB0>, <locked _thread.lock object at 0x000001FC49583C60>]
start loopstart loop 3start loop start loop 0 2  at:  at:1Wed May 26 09:31:06 2021
 at: Wed May 26 09:31:06 2021 Wed May 26 09:31:06 2021

at: Wed May 26 09:31:06 2021
done loop 1 at: Wed May 26 09:31:08 2021
done loop 2 at: Wed May 26 09:31:09 2021
done loop 0 at: Wed May 26 09:31:10 2021
done loop 3 at: Wed May 26 09:31:11 2021
all done at: Wed May 26 09:31:11 2021

Process finished with exit code 0

程序运行的的总时间是5s。main()函数最后一个循环的作用是等待所有子线程退出。

 

8.threading模块:

threading模块不仅提供了Thread类,还提供了各种非常好用的同步机制。_thread模块不支持守护线程,守护线程一般是一个等待客户请求的服务器,如果没有客户提出请求,就一直等着

如果设定一个线程为守护线程,就表示这个线程不重要,在进程退出时,不用等待这个线程退出。如果主线程退出时不用等待子线程完成,就需要设定这些线程的daemon属性,即在线程Thread.start()开始前,调用setDaemon()函数设定线程的daemon标志(Thread.setDaemon(True)),就表示这个线程不重要。

如果一定要等子线程执行完在退出主线程,什么都不用做或者显示调用(Thread.setDaemon(False))以保证daemon标志是false,可以调用Thread.isDaemon()函数判断daemon标志的值。新的子线程会继承父线程的daemon标志,整个python在所有非守护线程退出后才会结束,即进程中没有非守护线程存在时才结束。

 

threading模块除了包含_thread模块中的所有方法,还包含其他方法:

  • threading.currentThread()   #返回当前的线程变量
  • threading.enumerate()       #返回一个包含正在运行的线程列表,正在运行指的是线程启动后、结束前,不包括启动前和终止后的线程。
  • threading.activeCount()     #返回正在运行的线程数量,与len(threading.enumerate)有相同的结果

除了上面讲的使用方法外,线程模块提供了Thread类来处理线程,Thread类提供了以下方法:

  • run()                                #表示线程活动的方法
  • start()                             #启动线程活动
  • join([time])                     #等待至线程终止。这阻塞调用线程直至线程的join()方法被调用终止-正常退出或者抛出未处理的异常-或者是可选的超时发生
  • isAlive()                        #返回线程是否活动的
  • getName()                   #返回线程名
  • setName()                  #设置线程名

 

例3,使用threading模块创建线程
可以直接从threading.Thread继承创建一个新的子类,并实例化后调用start()方法启动新的线程,即它调用了线程的run()方法

import threading
import time
exitFlag=0

class myThread(threading.Thread):
    def __init__(self,threadID,name,counter):
        threading.Thread.__init__(self)
        self.threadID=threadID
        self.name =name
        self.counter=counter
    def run(self):
        print('开始线程:' + self.name)
        print_time(self.name,self.counter,5)
        print('退出线程:' + self.name)

def print_time(threadName,delay,counter):
    while counter:
        if exitFlag:
            threadName.exit()
        time.sleep(delay)
        print('%s: %s' % (threadName,time.ctime(time.time())))
        counter -=1

#创建新线程
thread1=myThread(1,'Thread-1',1)
thread2=myThread(2,'Thread-2',2)

#启动新线程
thread1.start()
thread2.start()
thread1.join()
thread2.join()
print('退出主线程')

##输出结果:
开始线程:Thread-1
开始线程:Thread-2
Thread-1: Wed May 26 09:41:39 2021
Thread-2: Wed May 26 09:41:40 2021
Thread-1: Wed May 26 09:41:40 2021
Thread-1: Wed May 26 09:41:41 2021
Thread-2: Wed May 26 09:41:42 2021
Thread-1: Wed May 26 09:41:42 2021
Thread-1: Wed May 26 09:41:43 2021
退出线程:Thread-1
Thread-2: Wed May 26 09:41:44 2021
Thread-2: Wed May 26 09:41:46 2021
Thread-2: Wed May 26 09:41:48 2021
退出线程:Thread-2
退出主线程

 

9.线程同步

如果多个线程共同对某个数据修改,则可能出现不可预料的结果,为了保证数据的正确性,需要对多个线程进行同步。 使用 Thread 对象的 Lock 和 Rlock 可以实现简单的线程同步,这两个对象都有 acquire 方法和 release 方法,对于那些需要每次只允许一个线程操作的数据,可以将其操作放到 acquire 和 release 方法之间。

多线程的优势在于可以同时运行多个任务(至少感觉起来是这样)。但是当线程需要共享数据时,可能存在数据不同步的问题。

 

考虑这样一种情况:一个列表里所有元素都是0,线程"set"从后向前把所有元素改成1,而线程"print"负责从前往后读取列表并打印。那么,可能线程"set"开始改的时候,线程"print"便来打印列表了,输出就成了一半0一半1,这就是数据的不同步。为了避免这种情况,引入了锁的概念。

锁有两种状态——锁定和未锁定。每当一个线程比如"set"要访问共享数据时,必须先获得锁定;如果已经有别的线程比如"print"获得锁定了,那么就让线程"set"暂停,也就是同步阻塞;等到线程"print"访问完毕,释放锁以后,再让线程"set"继续。 经过这样的处理,打印列表时要么全部输出0,要么全部输出1,不会再出现一半0一半1的尴尬场面。

 

例4:

import threading
import time

class myThread (threading.Thread):
    def __init__(self, threadID, name, counter):
        threading.Thread.__init__(self)
        self.threadID = threadID
        self.name = name
        self.counter = counter
    def run(self):
        print ("开启线程: " + self.name)
        # 获取锁,用于线程同步
        threadLock.acquire()
        print_time(self.name, self.counter, 3)
        # 释放锁,开启下一个线程
        threadLock.release()

def print_time(threadName, delay, counter):
    while counter:
        time.sleep(delay)
        print ("%s: %s" % (threadName, time.ctime(time.time())))
        counter -= 1

threadLock = threading.Lock()
threads = []

# 创建新线程
thread1 = myThread(1, "Thread-1", 1)
thread2 = myThread(2, "Thread-2", 2)

# 开启新线程
thread1.start()
thread2.start()

# 添加线程到线程列表
threads.append(thread1)
threads.append(thread2)

# 等待所有线程完成
for t in threads:
    t.join()
print ("退出主线程")


##输出结果:
开启线程: Thread-1
开启线程: Thread-2
Thread-1: Wed May 26 09:50:03 2021
Thread-1: Wed May 26 09:50:04 2021
Thread-1: Wed May 26 09:50:05 2021
Thread-2: Wed May 26 09:50:07 2021
Thread-2: Wed May 26 09:50:09 2021
Thread-2: Wed May 26 09:50:11 2021
退出主线程

 

10. 线程优先级队列( Queue)

Python 的 Queue 模块中提供了同步的、线程安全的队列类,包括FIFO(先入先出)队列Queue,LIFO(后入先出)队列LifoQueue,和优先级队列 PriorityQueue。

这些队列都实现了锁原语,能够在多线程中直接使用,可以使用队列来实现线程间的同步。

Queue 模块中的常用方法:

  • Queue.qsize()                                返回队列的大小
  • Queue.empty()                              如果队列为空,返回True,反之False
  • Queue.full()                                   如果队列满了,返回True,反之False
  • Queue.full                                     与 maxsize 大小对应
  • Queue.get([block[, timeout]])       获取队列,timeout等待时间
  • Queue.get_nowait()                    相当Queue.get(False)
  • Queue.put(item)                         写入队列,timeout等待时间
  • Queue.put_nowait(item)             相当Queue.put(item, False)
  • Queue.task_done()                    在完成一项工作之后,Queue.task_done()函数向任务已经完成的队列发送一个信号
  • Queue.join()                               实际上意味着等到队列为空,再执行别的操作

 

例5:

import queue
import threading
import time

exitFlag = 0

class myThread (threading.Thread):
    def __init__(self, threadID, name, q):
        threading.Thread.__init__(self)
        self.threadID = threadID
        self.name = name
        self.q = q
    def run(self):
        print ("开启线程:" + self.name)
        process_data(self.name, self.q)
        print ("退出线程:" + self.name)

def process_data(threadName, q):
    while not exitFlag:
        queueLock.acquire()
        if not workQueue.empty():
            data = q.get()
            queueLock.release()
            print ("%s processing %s" % (threadName, data))
        else:
            queueLock.release()
        time.sleep(1)

threadList = ["Thread-1", "Thread-2", "Thread-3"]
nameList = ["One", "Two", "Three", "Four", "Five"]
queueLock = threading.Lock()
workQueue = queue.Queue(10)
threads = []
threadID = 1

# 创建新线程
for tName in threadList:
    thread = myThread(threadID, tName, workQueue)
    thread.start()
    threads.append(thread)
    threadID += 1

# 填充队列
queueLock.acquire()
for word in nameList:
    workQueue.put(word)
queueLock.release()

# 等待队列清空
while not workQueue.empty():
    pass

# 通知线程是时候退出
exitFlag = 1

# 等待所有线程完成
for t in threads:
    t.join()
print ("退出主线程")


##输出结果:
开启线程:Thread-1
开启线程:Thread-2
开启线程:Thread-3
Thread-3 processing One
Thread-3 processing Two
Thread-2 processing Three
Thread-1 processing Four
Thread-3 processing Five
退出线程:Thread-2
退出线程:Thread-1
退出线程:Thread-3
退出主线程

 

 

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

了解python之进程与线程 的相关文章

随机推荐

  • STM32开发必备知识篇:串口DMA空闲中断

    随着撰写博客的深入 xff0c 笔者先初步打算把博客细分为四大板块 xff1a 1 FPGA基础知识篇 xff1b 2 FPGA 20个例程篇 xff1b 3 STM32开发必备知识篇 xff1b 4 STM32 10个项目篇 xff0c
  • 大端小端(Big- Endian和Little-Endian)

    字节序 xff08 Endian xff09 xff0c 大端 xff08 Big Endian xff09 xff0c 小端 xff08 Little Endian xff09 图文并茂 http www cppblog com tx7d
  • STM32程序设计规范浅析

    这篇博客写到 STM32基础知识篇 里 xff0c 一方面是一个很好地对过往工作的总结 xff0c 另一方面也是整个专栏撰写计划的开端 xff0c 古人云 xff1a 良好的开端是成功的一半 xff0c 在文章的最后详细地规划了整个专栏后期
  • C语言编程规范(头文件规范)

    C语言的规范使用 xff0c 有利于提高代码的清晰 简洁 可测试 安全 效率 可移植 xff0c 因此必须规范使用C语言编程 代 码 总 体 原
  • C语言变量和常量命名规则

    变量命名规则 原则 1 一个变量只有一 个功能 xff0c 不能把一个变量用作多个用途 2 结构单一 xff0c 不能设计面面俱到的数据结构 xff1b 结构的定义应该明确的描述一个对象 xff0c 去掉相关相不强的数据 xff1b 3 不
  • ROS+Gazebo----Unable to find uri[model:// ]

    基于ROS 43 Gazebo环境 xff0c 用roslaunch把sdf模型加载到gazebo仿真世界 目录结构如下 输入命令roslaunch my simulation my world launch 报错 xff1a 1 不接入网
  • 最完整介绍Visual C++ 6.0和Visual Studio 2022中的编译、生成和运行(CTRL+F7、F7、CTRL+F5)

    我是荔园微风 xff0c 作为一名在IT界整整25年的老兵 xff0c 经常被Visual C 43 43 6 0和Visual Studio 2022初学者问到程序写好后怎么使用编译调试菜单以及怎么使用CTRL 43 F7 F7 CTRL
  • 判断大小端的方法(java和c++)

    首先我们给出大小端的定义 小端 xff1a 较高的有效字节存放在较高的的存储器地址 xff0c 较低的有效字节存放在较低的存储器地址 大端 xff1a 较高的有效字节存放在较低的存储器地址 xff0c 较低的有效字节存放在较高的存储器地址
  • vscode配置c++代码提示补全

    vscode配置c 43 43 代码提示补全 在网上找了大半天 xff0c 说的方式都试过了 xff0c 都没有适合我的 xff0c 还是自己找stackoverflow靠谱点 34 editor rulers 34 80 一行限制80字符
  • 解决头文件相互包含问题的方法

    解决头文件相互包含问题的方法 所谓超前引用是指一个类型在定义之前就被用来定义变量和声明函数 一般情况下 xff0c C C 43 43 要求所有的类型必须在使用前被定义 xff0c 但是在一些特殊情况下 xff0c 这种要求无法满足 xff
  • C++ 中头文件相互包含问题的解决办法

    我们在写C 43 43 程序的时候 xff0c 常常要把不同的类的声明放置与不同的头文件中 xff0c 以提高代码的整洁性 xff0c 如此一来 xff0c 就难免会遇到头文件相互包含的问题 xff0c 也就是说 xff0c 假设我们有两个
  • Pixhawk_nuttx启动过程和启动文件

    lt span style 61 34 font family Arial Helvetica sans serif background color rgb 255 255 255 34 gt Pixhawk nuttx启动过程 lt s
  • 施密特触发器原理图解

    施密特触发器原理图解详细分析 重要特性 xff1a 施密特触发器具有如下特性 xff1a 输入电压有两个阀值VL VH xff0c VL施密特触发器通常用作缓冲器消除输入端的干扰 施密特波形图 施密特触发器也有两个稳定状态 xff0c 但与
  • Delphi 类库(DLL)动态调用与静态调用示例讲解

    在Delphi或者其它程序中我们经常需要调用别人写好的DLL类库 下面直接上示例代码演示如何进行动态和静态的调用方法 DLL动态调用与静态调用的例子 编译环境 Delphi XE 转载或编译请不要修改此文件
  • HTML中的Hack手段之条件注释

    通常WEB 的好处就是可以跨平台 但这个世界偏偏有个另类 就是IE 浏览器 在平常做HTML 设计时 xff0c 有时需要为IE 的表示差异而不得不使用一些Hack 手段 条件注释就是这类手段之一 条件注释是IE 浏览器的 专利 也就是说我
  • JavaScript函数中的arguments对象

    ECMAScript标准中 xff0c 每个函数都有一个特殊的内置对象arguments arguments对象是一个类Array对象 object 用以保存函数接收到的实参副本 一 内置特性 说它是一个内置对象是因为我们在创建函数时并没有
  • JavaScript函数之高阶函数

    高阶函数 xff08 higher order function xff09 如果一个函数接收的参数为或返回的值为函数 xff0c 那么我们可以将这个函数称为高阶函数 众所周知 xff0c JavaScript是一种弱类型的语言 JavaS
  • 前端优化建议:合理利用JavaScript的条件运算符

    在最近的项目中要使用到一个格式化文件大小的算法 xff0c 于是不假思索写了如下代码 function formatSize size if size lt 1024 return size 43 34 B 34 else if size
  • 了解python之面向对象

    了解python之面向对象 面向对象概念 xff1a 面向对象编程 xff08 Object Oriented Programming xff0c 简称OOP xff09 是一种程序涉及思想 OOP把对象作为程序的基本单元 xff0c 一个
  • 了解python之进程与线程

    了解python之进程与线程 本文虽然叫作 了解python进程与线程 xff0c 但还是有点难度的 可以先查阅另外一篇文字 xff0c 快速入门 Python快速入门多线程与多进程 1 进程 进程 xff08 Process xff0c