不爱胡萝卜的仓鼠 发表于 2024-10-3 00:47

[嘉楠 CanMV K230]测评 ⑤多线程

<div class='showpostmsg'><p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;在实际开发过程中,只靠一个while1是不够用的,需要有多线程。python中是带有多线程功能的(我平时FreeRTOS用的比较多,叫任务/task已经习惯了,后续可能顺手就打成任务/task,请见谅)</p>

<p>&nbsp; &nbsp; &nbsp; &nbsp; 可惜K230上不支持threading,只能支持_thread</p>

<div style="text-align: center;"></div>

<p>&nbsp; &nbsp; &nbsp; &nbsp; 在python官方文档关于_thread的内容如下:<a href="https://docs.python.org/3.5/library/_thread.html#module-thread" rel="noopener noreferrer" target="_blank">https://docs.python.org/3.5/library/_thread.html#module-thread</a></p>

<p>&nbsp;</p>

<p><strong><span style="font-size:24px;">一.创建线程并启动</span></strong></p>

<p>&nbsp; &nbsp; &nbsp; &nbsp; 接下来我们就来写一个demo,运行后创建2个task,第一个task为闪灯task,第二个为串口task。这两个task中执行的内容就是之前我们写过的</p>

<pre>
<code class="language-python">import _thread
import time
from machine import Pin
from machine import UART
from machine import FPIOA

fpioa = FPIOA()

#闪灯task
def task_blink():
    #初始化52号引脚为输出模式,不拉高,驱动能力为默认的7
    LED = Pin(52, Pin.OUT, pull=Pin.PULL_NONE, drive=7)
   
    while True:
      #开灯
      LED.value(1)
      time.sleep(1)
      #关灯
      LED.value(0)
      time.sleep(1)
      
#串口task
def task_uart():
    #初始化串口2
    fpioa.set_function(11,FPIOA.UART2_TXD)
    fpioa.set_function(12,FPIOA.UART2_RXD)
    my_uart = UART(UART.UART2, baudrate=115200, bits=UART.EIGHTBITS, parity=UART.PARITY_NONE, stop=UART.STOPBITS_ONE)
   
    #发送数据测试
    my_uart.write('UART2 send test\r\n')
   
    #一直接收数据
    while True:
      #读取一行,并以一个换行符结束。
      recv_data = my_uart.readline()
      #如果有读到数据
      if recv_data != b'':
            #把收到数据通过日志串口打印出来
            print(recv_data)
            #把收到的数据原样从串口2发出去
            my_uart.write(recv_data)
      time.sleep(0.1)
   

#创建闪灯task和串口task
_thread.start_new_thread(task_blink, ())
_thread.start_new_thread(task_uart, ())


while True:
    time.sleep(0.01) #防止CPU满跑
</code></pre>

<p>f7c8c71340c23ee5ae56892e0a92d4da<br />
&nbsp;</p>

<p><strong><span style="font-size:24px;">二.线程停止</span></strong></p>

<p><span style="font-size:16px;"><b>2.1 由别的task摧毁某个task</b></span></p>

<p>&nbsp; &nbsp; &nbsp; &nbsp; 在python的介绍中,_thread并没有制定摧毁某个task的接口,只有在task调用的函数退出运行,也就是要退出task中的while1</p>

<div style="text-align: center;"></div>

<p>&nbsp; &nbsp; &nbsp; &nbsp; 所以,我们可以使用一个flag,来决定while1是否可以运行,我这边偷懒,直接用一个全局变量,当然在多线程的环境下,各个task之间通讯用全局变量不合适,用队列比较好,不过我这边的使用场景比较简单,也不存在竞争,可以偷懒用一下</p>

<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;我们来改造一下刚才的代码,当串口收到&ldquo;STOP&rdquo;后,把闪灯的task摧毁。收到&ldquo;START&rdquo;后,开始闪灯TASK</p>

<pre>
<code class="language-python">import _thread
import time
from machine import Pin
from machine import UART
from machine import FPIOA

fpioa = FPIOA()

# 全局变量作为标志位
task_blink_run_flag = False

#闪灯task
def task_blink():
    global task_blink_run_flag
    #初始化52号引脚为输出模式,不拉高,驱动能力为默认的7
    LED = Pin(52, Pin.OUT, pull=Pin.PULL_NONE, drive=7)
   
    while task_blink_run_flag:
      #开灯
      LED.value(1)
      time.sleep(1)
      #关灯
      LED.value(0)
      time.sleep(1)
      
#串口task
def task_uart():
    global task_blink_run_flag
    #初始化串口2
    fpioa.set_function(11,FPIOA.UART2_TXD)
    fpioa.set_function(12,FPIOA.UART2_RXD)
    my_uart = UART(UART.UART2, baudrate=115200, bits=UART.EIGHTBITS, parity=UART.PARITY_NONE, stop=UART.STOPBITS_ONE)
   
    #发送数据测试
    my_uart.write('UART2 send test\r\n')
   
    #一直接收数据
    while True:
      #读取一行,并以一个换行符结束。
      recv_data = my_uart.readline()
      #如果有读到数据
      if recv_data != b'':
            #把收到数据通过日志串口打印出来
            print(recv_data)
      
      if recv_data == b'STOP\r\n':
            print('recv stop')
            # 避免task_blink被多次stop
            if task_blink_run_flag == True:
                task_blink_run_flag = False
            else:
                print('stop task_blink fail')
      elif recv_data == b'START\r\n':
            print('recv START')
            # 避免task_blink被多次start
            if task_blink_run_flag == False:
                task_blink_run_flag = True
                _thread.start_new_thread(task_blink, ())
            else:
                print('start task_blink fail')
      time.sleep(0.1)
   

#创建闪灯task和串口task
task_blink_run_flag = True
_thread.start_new_thread(task_blink, ())
_thread.start_new_thread(task_uart, ())


while True:
    time.sleep(0.01) #防止CPU满跑
</code></pre>

<p>98beafa1ea045a9f74da5438547a3984</p>

<p>&nbsp;</p>

<p><strong><span style="font-size:16px;">2.2 TASK自己摧毁自己</span></strong></p>

<p>&nbsp; &nbsp; &nbsp; &nbsp; 按照上述的思路,我们可以在task内部加一个变量,用于控制task中的while1,改造代码如下(为了看得清楚,其他部分我没动,新增了一个task)</p>

<pre>
<code class="language-python">import _thread
import time
from machine import Pin
from machine import UART
from machine import FPIOA

fpioa = FPIOA()

# 全局变量作为标志位
task_blink_run_flag = False

#闪灯task
def task_blink():
    global task_blink_run_flag
    #初始化52号引脚为输出模式,不拉高,驱动能力为默认的7
    LED = Pin(52, Pin.OUT, pull=Pin.PULL_NONE, drive=7)
   
    while task_blink_run_flag:
      #开灯
      LED.value(1)
      time.sleep(1)
      #关灯
      LED.value(0)
      time.sleep(1)
      
#串口task
def task_uart():
    global task_blink_run_flag
    #初始化串口2
    fpioa.set_function(11,FPIOA.UART2_TXD)
    fpioa.set_function(12,FPIOA.UART2_RXD)
    my_uart = UART(UART.UART2, baudrate=115200, bits=UART.EIGHTBITS, parity=UART.PARITY_NONE, stop=UART.STOPBITS_ONE)
   
    #发送数据测试
    my_uart.write('UART2 send test\r\n')
   
    #一直接收数据
    while True:
      #读取一行,并以一个换行符结束。
      recv_data = my_uart.readline()
      #如果有读到数据
      if recv_data != b'':
            #把收到数据通过日志串口打印出来
            print(recv_data)
      
      if recv_data == b'STOP\r\n':
            print('recv stop')
            # 避免task_blink被多次stop
            if task_blink_run_flag == True:
                task_blink_run_flag = False
            else:
                print('stop task_blink fail')
      elif recv_data == b'START\r\n':
            print('recv START')
            # 避免task_blink被多次start
            if task_blink_run_flag == False:
                task_blink_run_flag = True
                _thread.start_new_thread(task_blink, ())
            else:
                print('start task_blink fail')
      time.sleep(0.1)
   

def task_destory_by_self():
    flag = True
    cnt = 0
    while flag:
      print('task_destory_by_self running')
      cnt += 1
      time.sleep(1)
      if cnt &gt;= 5:
            flag = False
    print('task_destory_by_self QUIT')

#创建闪灯task和串口task
task_blink_run_flag = True
_thread.start_new_thread(task_blink, ())
_thread.start_new_thread(task_uart, ())
_thread.start_new_thread(task_destory_by_self, ())


while True:
    time.sleep(0.01) #防止CPU满跑
</code></pre>

<p>可以看到,task_destory_by_self running打印 5次后,打印了task_destory_by_self QUIT,说明task是已经被摧毁了</p>

<p>091ca09e2e1082e2b877a20a2f77fc3c<br />
&nbsp;</p>

<p>&nbsp; &nbsp; &nbsp; &nbsp; 在python的文档中,还有一种方法,就是调用_thread.exit()函数,可以在task中直接退出,当执行这个函数后,TASK立刻被摧毁,这个函数后面的代码都不会被执行。代码如下</p>

<pre>
<code class="language-python">import _thread
import time
from machine import Pin
from machine import UART
from machine import FPIOA

fpioa = FPIOA()

# 全局变量作为标志位
task_blink_run_flag = False

#闪灯task
def task_blink():
    global task_blink_run_flag
    #初始化52号引脚为输出模式,不拉高,驱动能力为默认的7
    LED = Pin(52, Pin.OUT, pull=Pin.PULL_NONE, drive=7)
   
    while task_blink_run_flag:
      #开灯
      LED.value(1)
      time.sleep(1)
      #关灯
      LED.value(0)
      time.sleep(1)
      
#串口task
def task_uart():
    global task_blink_run_flag
    #初始化串口2
    fpioa.set_function(11,FPIOA.UART2_TXD)
    fpioa.set_function(12,FPIOA.UART2_RXD)
    my_uart = UART(UART.UART2, baudrate=115200, bits=UART.EIGHTBITS, parity=UART.PARITY_NONE, stop=UART.STOPBITS_ONE)
   
    #发送数据测试
    my_uart.write('UART2 send test\r\n')
   
    #一直接收数据
    while True:
      #读取一行,并以一个换行符结束。
      recv_data = my_uart.readline()
      #如果有读到数据
      if recv_data != b'':
            #把收到数据通过日志串口打印出来
            print(recv_data)
      
      if recv_data == b'STOP\r\n':
            print('recv stop')
            # 避免task_blink被多次stop
            if task_blink_run_flag == True:
                task_blink_run_flag = False
            else:
                print('stop task_blink fail')
      elif recv_data == b'START\r\n':
            print('recv START')
            # 避免task_blink被多次start
            if task_blink_run_flag == False:
                task_blink_run_flag = True
                _thread.start_new_thread(task_blink, ())
            else:
                print('start task_blink fail')
      time.sleep(0.1)
   

def task_destory_by_self():
    cnt = 0
    while True:
      print('task_destory_by_self running')
      cnt += 1
      time.sleep(1)
      if cnt &gt;= 5:
            print('task_destory_by_self exit')
            _thread.exit()
    print('task_destory_by_self QUIT')

#创建闪灯task和串口task
task_blink_run_flag = True
_thread.start_new_thread(task_blink, ())
_thread.start_new_thread(task_uart, ())
_thread.start_new_thread(task_destory_by_self, ())


while True:
    time.sleep(0.01) #防止CPU满跑
</code></pre>

<p>9450d0deba6e2ef614d7a978afc4c8d5<br />
&nbsp;</p>

<p>&nbsp;</p>

<p>&nbsp;</p>
</div><script>                                        var loginstr = '<div class="locked">查看本帖全部内容,请<a href="javascript:;"   style="color:#e60000" class="loginf">登录</a>或者<a href="https://bbs.eeworld.com.cn/member.php?mod=register_eeworld.php&action=wechat" style="color:#e60000" target="_blank">注册</a></div>';
                                       
                                        if(parseInt(discuz_uid)==0){
                                                                                                (function($){
                                                        var postHeight = getTextHeight(400);
                                                        $(".showpostmsg").html($(".showpostmsg").html());
                                                        $(".showpostmsg").after(loginstr);
                                                        $(".showpostmsg").css({height:postHeight,overflow:"hidden"});
                                                })(jQuery);
                                        }                </script><script type="text/javascript">(function(d,c){var a=d.createElement("script"),m=d.getElementsByTagName("script"),eewurl="//counter.eeworld.com.cn/pv/count/";a.src=eewurl+c;m.parentNode.insertBefore(a,m)})(document,523)</script>
页: [1]
查看完整版本: [嘉楠 CanMV K230]测评 ⑤多线程