核心内容摘要
【STM8S】深入解析FLASH与EEPROM的读写机制及优化技巧
ä½ å¥½æˆ‘æ˜¯å®‰ç„¶æ— è™�。和我一起为高质é‡�人生而ä¸�æ‡ˆå¥‹æ–—ã€‚æ–‡ç« ç›®å½•å¤šçº¿ç¨‹å¤šçº¿ç¨‹å¼•å…¥2ç§�æ–¹å¼�创建线程线程间的通信并å�‘æ�§åˆ¶-Semaphoreçº¿ç¨‹æ± done()result()cancel()as_completed()map()wait()withè¯å�¥å¤šçº¿ç¨‹å¤šçº¿ç¨‹å¼•入之å‰�我有写过一篇关äº� å¤šçº¿ç¨‹å’Œå¤šè¿›ç¨‹çš„æ–‡ç« :31天Python入门——第24天:挑战一å�£æ°”把多线程和多进程讲æ˜�白没看过的è€�é“�, å�¯ä»¥å…ˆé˜…读上é�¢è¿™ç¯‡æ–‡ç« , è¿™æ ·çš„è¯�就有了基础, 方便下é�¢å†…容的å¦ä¹ .多线程: 适用äº� IO 密集å�‹ä»£ç �多进程: 适用äº� CPU 密集å�‹ä»£ç �爬虫, 主è¦�是 网络IO.GIL é�‡åˆ° IOæ“�作的时候, 会释放.ok, 有了å‰�é�¢çš„基础, 下é�¢æˆ‘们看这段 多线程代ç �:print(主线程执行代ç �)# ä»� threading 库ä¸å¯¼å…¥Threadç±»fromthreadingimportThreadfromtimeimportsleep# 定义一个函数作为新线程执行的入å�£å‡½æ•°defthreadFunc(arg1,arg
:print(å�线程 开始)print(f线程函数å�‚数是{arg1},{arg2})sleep(
print(å�线程 结æ�Ÿ)# 创建 Thread 类的å®�例对象threadThread(# target å�‚æ•° 指定 新线程è¦�执行的函数# 注æ„�这里指定的函数对象å�ªèƒ½å†™ä¸€ä¸ªå��å—ä¸�能å��é�¢åŠ æ‹¬å�·# 如æ�œåŠ æ‹¬å�·å°±æ˜¯ç›´æ�¥åœ¨å½“å‰�线程调用执行而ä¸�æ˜¯åœ¨æ–°çº¿ç¨‹ä¸æ‰§è¡Œäº†targetthreadFunc,# 如æ�œ 新线程函数需è¦�å�‚数在 args里é�¢å¡«å…¥å�‚æ•°# 注æ„�å�‚数是元组 如æ�œå�ªæœ‰ä¸€ä¸ªå�‚æ•°å��é�¢è¦�有逗å�·åƒ�è¿™æ · args(å�‚æ•°1,)args(å�‚æ•°1,å�‚æ•°
)# 执行start 方法就会创建新线程# 并且新线程会å�»æ‰§è¡Œå…¥å�£å‡½æ•°é‡Œé�¢çš„代ç �。# 这时候 这个进程 有两个线程了。thread.start()# 主线程的代ç �执行 å�线程对象的join方法# 就会ç‰å¾…å�线程结æ�Ÿæ‰�ç»§ç»æ‰§è¡Œä¸‹é�¢çš„代ç �thread.join()print(主线程结æ�Ÿ)这里有两点需è¦�强调说æ˜�:创建了 Thread å®�例对象之å��, 这时新的线程还没有创建, 需è¦�调用 Thread 对象的 start()方法, è¿™æ ·æ–°çš„çº¿ç¨‹æ‰�创建æˆ�功, 并开始执行 threadFunc å…¥å�£å‡½æ•°é‡Œé�¢çš„代ç �.有的时候, 一个线程需è¦�ç‰å¾…其他线程结æ�Ÿ, 比如需è¦�æ ¹æ�®å…¶ä»–线程è¿�行结æ�Ÿå��的结æ�œè¿›è¡Œå¤„ç�†, 这时å�¯ä»¥ä½¿ç”¨ Thread 对象的 join() 方法, ç‰å¾…对应的线程完æˆ�, æ‰�执行å��ç»ä»£ç �.好的, ç�†è§£äº†ä¸Šé�¢çš„代ç �之å��, å°±å�¯ä»¥ç»§ç»å��é�¢çš„å¦ä¹ 了.2ç§�æ–¹å¼�创建线程写爬虫的时候, 有一个简å�•网站å�—里é�¢æœ‰ 列表页和详情页.importthreadingdefget_list_html(url):print(开始è�·å�–列表页)time.sleep(
# 模拟网络请求.print(已���到��列表页)defget_detail_html(url):print(开始��详情页)time.sleep(
# 模拟网络请求.print(已���到��详情页)urlwww.baidu.comt1threading.Thread(targetget_list_html,args(url,))t2threading.Thread(targetget_detail_html,args(url,))t
start()t
start()t
join()t
join()è¿�行结æ�œæ˜¯:开始è�·å�–列表页 开始è�·å�–详情页 å·²ç»�è�·å�–到列表页 å·²ç»�è�·å�–到详情页这其å®�是 函数å¼�创建线程的方å¼�, 还有一ç§� é�¢å�‘对象å¼� 通过继承 threading.Thread 类并é‡�写 run()方法的方å¼�.ok, 下é�¢æˆ‘们用é�¢å�‘对象å¼�的方å¼�对上é�¢çš„代ç �进行改写:importthreadingimporttimeclassListHtmlThread(threading.Thread):def__init__(self,url):super().__init__()self.urlurl# ä¿�å˜å�‚数到å®�例å±�性defrun(self):print(开始è�·å�–列表页)time.sleep(
# 模拟网络请求print(已���到列表页)classDetailHtmlThread(threading.Thread):def__init__(self,url):super().__init__()self.urlurldefrun(self):print(开始��详情页)time.sleep(
# 模拟网络请求print(已���到详情页)# 使用urlwww.baidu.comt1ListHtmlThread(url)t2DetailHtmlThread(url)t
start()t
start()t
join()t
join()è�ªæ˜�çš„è€�é“�å�¯èƒ½å·²ç»�å�‘ç�°äº†, å…¶å®� run()方法 就是在线程ä¸è¿�行的代ç �体, 也就相当äº� 函数å¼�创建线程时的 ä¼ ç»™target的那个函数.之å‰�说过 daemon线程 的概念(也å�«å®ˆæŠ¤çº¿ç¨‹), å‰�é�¢å‡½æ•°å¼�代ç �è¿�è¡Œä¹Ÿæ˜¯ä¸€æ ·, 主线程得ç‰2ç§’å��å�线程结æ�Ÿ, 主线程æ‰�能结æ�Ÿ.è¿™æ˜¯å› ä¸º:Python 程åº�ä¸å½“所有的 é��daemon线程 都结æ�Ÿäº†, 整个程åº�æ‰�会结æ�Ÿ.主线程是é��daemon线程, å�¯åŠ¨çš„å�线程缺çœ�也是 é��daemon线程.所以, è¦�ç‰åˆ°ä¸»çº¿ç¨‹å’Œå�线程都结æ�Ÿ, 程åº�æ‰�会结æ�Ÿ.我们å�¯ä»¥åœ¨åˆ›å»ºå�线程的时候, 设置daemonå�‚数值为 True, 比如之å‰�的代ç �改写:# æ–¹å¼�一: 在定义å®�例对象的时候设置 daemonTruethreadThread(targetthreadFunc,daemonTrue# 设置新线程为daemon线程)# æ–¹å¼�二: 在线程å�¯åЍå‰�调用 .setDeamon(True)方法threadThread(targetthreadFunc)thread.setDaemon(True)# 需è¦�注æ„�的点: 守护线程è¦�在线程å�¯åЍ之å‰�设置thread.start()好的, ç�†è§£äº†ä¸Šé�¢å‡½æ•°å¼�çš„, 那么é�¢å�‘对象å¼�的就很简å�•了, å�ªè¦�è¿™æ ·:...t1ListHtmlThread(url)t2DetailHtmlThread(url)t
setDaemon(True)t
setDaemon(True)t
start()t
start()线程间的通信在之å‰�çš„æ–‡ç« ä¸æˆ‘们有说过 通过é”�机制æ�¥å¯¹å…±äº«æ•°æ�®çš„访问æ�§åˆ¶, 忘记的è€�é“�å�¯ä»¥å†�å›�顾一下:31天Python入门——第24天:挑战一å�£æ°”把多线程和多进程讲æ˜�白好的, ç�°åœ¨æˆ‘们知é�“:在多线程ç�¯å¢ƒä¸, 所有线程共享å�Œä¸€ä¸ªè¿›ç¨‹çš„内å˜ç©ºé—´. 但是由äº�线程切æ�¢å¾—é��常快, å› æ¤å¤šä¸ªçº¿ç¨‹å�Œæ—¶è®¿é—®å’Œä¿®æ”¹å…±äº«èµ„æº�æ—¶, 很容易出ç�°æ•°æ�®ç«�争问题, 这就å�¯èƒ½å�‘生数æ�®ä¸�一致的错误.为了é�¿å…�è¿™ç§�问题, 需è¦�使用é”�或其他的å�Œæ¥æœºåˆ¶ æ�¥ç¡®ä¿�å�Œä¸€æ—¶é—´å�ªæœ‰ä¸€ä¸ªçº¿ç¨‹å�¯ä»¥è®¿é—®å…±äº«èµ„æº�.æ�¥ä¸‹æ�¥, 我们使用本身就是线程安全的数æ�®ç»“æ�„ Queue, 通过它æ�¥ä»£æ›¿å…±äº«æ•°æ�®çš„访问.ok, 下é�¢æœ‰ä¸€ä¸ªç”Ÿäº§è€…消费者模å�‹ (生产者线程在生产, 消费者线程在消费) :importthreadingimporttimefromqueueimportQueue,Empty# 通过安全的队列æ�¥é€šä¿¡.list_statusFalse# 生产者classGetListHtml(threading.Thread):def__init__(self,url_queue:Queue):super(GetListHtml,self).__init__()self.url_queueurl_queuedefrun(self):print(开始è�·å�–列表页)try:foriinrange(
:time.sleep(
0.
# 模拟网络请求.urlfwww.yrx.com/{i}# Queue.join()里有一个计数器, 这一个计数器会在put的时候åŠ
在�一次task_done的时候�1self.url_queue.put(url)print(已���到��列表页)finally:globallist_status list_statusTrue# 消费者classGetDetailHtml(threading.Thread):def__init__(self,url_queue:Queue):super(GetDetailHtml,self).__init__()self.url_queueurl_queuedefrun(self):print(开始��详情页)whileTrue:try:urlself.url_queue.get(timeout
# Queue.join()里有一个计数器, 这一个计数器会在put的时候åŠ
在�一次task_done的时候�1self.url_queue.task_done()print(f已���到��详情页-{url})time.sleep(
# 模拟网络请求.# 当消费者消费比生产者快的时候,Queue里�的一定会出�为空的情况, 这时候消费者��消费会出�出�Empty异常exceptEmpty:# 想一个�法, 让程�知�生产者已�结�了.globallist_statusprint(list_status)iflist_status:print(详情页已抓完)breakif__name____main__:start_timetime.time()url_queueQueue(
# 一个生产者t1GetListHtml(url_queue)# 两个消费者t2GetDetailHtml(url_queue)t3GetDetailHtml(url_queue)# 创建线程并�动t
start()t
start()t
start()# 主线程阻å¡�ç‰å¾…å�线程执行完毕t
join()t
join()t
join()# join: 阻å¡�队列. 在主线程阻å¡�.# join里有一个计数器, 这一个计数器会在put的时候åŠ
在�一次task_done的时候�
# 当这个计数器是0的时候. 它会释放这个阻�.url_queue.join()print(time.time()-start_time)�行结�是:开始��列表页 开始��详情页 开始��详情页 已���到��列表页 已���到��详情页-www.yrx.com/0 False 已���到��列表页 已���到��详情页-www.yrx.com/1 已���到��列表页 已���到��详情页-www.yrx.com/2 已���到��列表页已���到��详情页-www.yrx.com/3 已���到��列表页已���到��详情页-www.yrx.com/4 已���到��列表页 已���到��详情页-www.yrx.com/5 已���到��列表页已���到��详情页-www.yrx.com/6 已���到��列表页 已���到��详情页-www.yrx.com/7 已���到��列表页已���到��详情页-www.yrx.com/8 已���到��列表页已���到��详情页-www.yrx.com/9 True 详情页已抓完 True 详情页已抓完
050168991088867å¹¶å�‘æ�§åˆ¶-Semaphore先看下é�¢çš„代ç �:importthreadingimporttimeclassGetHtml(threading.Thread):def__init__(self,url):super().__init__()self.urlurldefrun(self):time.sleep(
print(f��详情页�功:{self.url})classSendUrl(threading.Thread):def__init__(self):super().__init__()defrun(self):foriinrange(
:urlwww.yrx.com/{}.format(i)get_html_threadGetHtml(urlurl)get_html_thread.start()if__name____main__:send_urlSendUrl()send_url.start()�行结�是:...... ��详情页�功: www.yrx.com/9 ��详情页�功: www.yrx.com/11 ��详情页�功: www.yrx.com/12 ��详情页�功: www.yrx.com/19 ��详情页�功: www.yrx.com/23 ��详情页�功: www.yrx.com/22 ��详情页�功: www.yrx.com/28 ��详情页�功: www.yrx.com/29如� for i in range(
: 创建的线程数更多呢, 比如是 30000, 那么代ç �是有问题的, 系统很å�¯èƒ½å› 为ä¸�会创建这么多线程而出ç�°å¼‚常错误, 所以这个时候, 我们就需è¦�使用 Semaphore 对并å�‘数进行æ�§åˆ¶.好的, æ�¥ä¸‹æ�¥æˆ‘们使用 Semaphore æ�¥æ�§åˆ¶å¹¶å�‘.改写代ç �如下:# Semaphore, 用äº�æ�§åˆ¶å¹¶å�‘, é”�.importthreadingimporttimeclassGetHtml(threading.Thread):def__init__(self,url,semaphore):super().__init__()self.urlurl self.semaphoresemaphoredefrun(self):time.sleep(
print(f��详情页�功:{self.url})# 释放�的�置self.semaphore.release()classSendUrl(threading.Thread):def__init__(self,semaphore:threading.Semaphore):super().__init__()self.semaphoresemaphoredefrun(self):foriinrange(
:urlwww.yrx.com/{}.format(i)# åŠ é”�çš„ä½�ç½®self.semaphore.acquire()get_html_threadGetHtml(urlurl,semaphoreself.semaphore)get_html_thread.start()if__name____main__:# 使用 Semaphore æ�¥æ�§åˆ¶å¹¶å�‘æ•°æ¯�次是5个线程semaphorethreading.Semaphore(
send_urlSendUrl(semaphore)send_url.start()使用 threading.Semaphore(
对代ç �改写了之å��, å�³ä½¿åœ¨å¾ªç�¯é‡Œè®¾ç½®äº†30000个线程, æ¯�次并å�‘数都是5个.çº¿ç¨‹æ± å‰�é�¢å·²ç»�说了多线程, 为什么还需è¦�çº¿ç¨‹æ± å‘¢?è¿™æ˜¯å› ä¸ºå¤šçº¿ç¨‹åœ¨ä¸€äº›åœºæ™¯ä¸‹æœ‰ä¸�足的地方, 我们先æ�¥çœ‹çœ‹ 线程的执行顺åº�:调用 start() 新建线程 —— 准备就绪状æ€� —— ç‰å¾…æ“�作系统的调度, CPU资æº� (消耗资æº�, 系统资æº�, CPU, 内å˜)é�‡åˆ° IO æ“�作时, GIL会释放, 线程会切æ�¢, è¿™æ ·çš„è¯�å�ˆä¼šæ‰§è¡Œä¸Šé�¢çš„(调用start()新建线程…)调用 run()方法执行完线程里的代ç � (å›�收资æº�)è¿™æ ·ä¸€æ�¥, 创建线程数很多的è¯�, 频ç¹�消耗资æº�ã€�å›�收资æº�导致效ç�‡å¾ˆä½�, 如æ�œæƒ³è¦�é�¿å…�上é�¢çš„问题, å�¯ä»¥çœ‹çœ‹ä¸‹é�¢çº¿ç¨‹æ± çš„å®�ç�°.首先, çº¿ç¨‹æ± çš„å¥½å¤„æ˜¯:æ��å�‡æ€§èƒ½, é‡�用资æº�, 有效地é�¿å…�了创建线程过多.主线程ä¸å�¯ä»¥è�·å�–到æŸ�1个线程的返å›�值, 还å�¯ä»¥è�·å�–线程的è¿�行状æ€�.æ�¥å�£ä¸€è‡´ (线程ã€�进程和å��ç¨‹ä½¿ç”¨äº†ä¸€æ ·çš„è®¾è®¡æ¨¡å¼� .Future), å¦ä¹ æˆ�本更ä½�, 代ç �维护æˆ�本ä½�.ok, 有了上é�¢çš„æ¦‚念, 我们æ�¥çœ‹çœ‹è¿™æ®µçº¿ç¨‹æ± 的基础代ç �:importtimefromconcurrent.futuresimportThreadPoolExecutordeftask_cost_time(times):time.sleep(times)print(f程åº�花费了{times}ç§’)returntimes executorThreadPoolExecutor(
task1executor.submit(task_cost_time,
task2executor.submit(task_cost_time,
我们直æ�¥è¿�行上é�¢çš„代ç �, 会得到:程åº�花费了2ç§’ 程åº�花费了3秒需è¦�注æ„�的是, submit()方法是里是自动执行start()å�¯åŠ¨çº¿ç¨‹çš„, submit()方法的返å›�值是 Future 对象, 并且 submit()方法是ä¸�阻å¡�çš„. 也就是说, 主线程并ä¸�会 join ç‰å¾…上é�¢çš„两个线程执行结æ�Ÿæ‰�会执行å��é�¢çš„代ç �.done()我们使用 done()方法æ�¥åˆ¤æ–线程是å�¦æ‰§è¡Œå®Œæ¯•:...print(task
done())time.sleep(
3.